feat(explorer,governance): always allow selecting a node, add node guard (#3678)
This commit is contained in:
parent
76ddf45f4c
commit
bded1d32ba
@ -1,9 +1,18 @@
|
|||||||
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment';
|
import {
|
||||||
|
AppFailure,
|
||||||
|
NetworkLoader,
|
||||||
|
NodeGuard,
|
||||||
|
NodeSwitcherDialog,
|
||||||
|
useEnvironment,
|
||||||
|
useInitializeEnv,
|
||||||
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
||||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
import { router } from './routes/router-config';
|
import { router } from './routes/router-config';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
const splashLoading = (
|
const splashLoading = (
|
||||||
<Splash>
|
<Splash>
|
||||||
@ -12,10 +21,23 @@ const splashLoading = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const { VEGA_URL } = useEnvironment();
|
||||||
|
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
|
||||||
|
(store) => [store.dialogOpen, store.setDialogOpen]
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<TendermintWebsocketProvider>
|
<TendermintWebsocketProvider>
|
||||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
<NodeGuard
|
||||||
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
|
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||||
|
>
|
||||||
|
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||||
|
</NodeGuard>
|
||||||
|
<NodeSwitcherDialog
|
||||||
|
open={nodeSwitcherOpen}
|
||||||
|
setOpen={setNodeSwitcherOpen}
|
||||||
|
/>
|
||||||
</NetworkLoader>
|
</NetworkLoader>
|
||||||
</TendermintWebsocketProvider>
|
</TendermintWebsocketProvider>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
|
import {
|
||||||
|
useEnvironment,
|
||||||
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { ENV } from '../../config/env';
|
import { ENV } from '../../config/env';
|
||||||
|
|
||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
|
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
|
||||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
const setNodeSwitcherOpen = useNodeSwitcherStore(
|
||||||
|
(store) => store.setDialogOpen
|
||||||
|
);
|
||||||
|
|
||||||
const { screenSize } = useScreenDimensions();
|
const { screenSize } = useScreenDimensions();
|
||||||
const showFullFeedbackLabel = useMemo(
|
const showFullFeedbackLabel = useMemo(
|
||||||
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||||
@ -15,46 +21,40 @@ export const Footer = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
<div className="flex justify-between gap-2 align-middle">
|
||||||
<div className="flex justify-between gap-2 align-middle">
|
{GIT_COMMIT_HASH && (
|
||||||
{GIT_COMMIT_HASH && (
|
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
<p data-testid="git-commit-hash">
|
||||||
<p data-testid="git-commit-hash">
|
{t('Version')}:{' '}
|
||||||
{t('Version')}:{' '}
|
<Link
|
||||||
<Link
|
href={
|
||||||
href={
|
GIT_ORIGIN_URL
|
||||||
GIT_ORIGIN_URL
|
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
: undefined
|
||||||
: undefined
|
}
|
||||||
}
|
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
>
|
||||||
>
|
{GIT_COMMIT_HASH}
|
||||||
{GIT_COMMIT_HASH}
|
</Link>
|
||||||
</Link>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
|
||||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
|
||||||
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
|
||||||
{t('Change')}
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex pl-2 content-center">
|
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||||
<ExternalLink href={ENV.addresses.feedback}>
|
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
||||||
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
|
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
||||||
</ExternalLink>
|
{t('Change')}
|
||||||
</div>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
|
||||||
<NodeSwitcherDialog
|
<div className="flex pl-2 content-center">
|
||||||
open={nodeSwitcherOpen}
|
<ExternalLink href={ENV.addresses.feedback}>
|
||||||
setOpen={setNodeSwitcherOpen}
|
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
|
||||||
/>
|
</ExternalLink>
|
||||||
</>
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ import {
|
|||||||
useEnvironment,
|
useEnvironment,
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
useInitializeEnv,
|
useInitializeEnv,
|
||||||
|
NodeGuard,
|
||||||
|
AppFailure,
|
||||||
|
NodeSwitcherDialog,
|
||||||
|
useNodeSwitcherStore,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { ENV } from './config';
|
import { ENV } from './config';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
@ -48,6 +52,7 @@ import {
|
|||||||
TELEMETRY_ON,
|
TELEMETRY_ON,
|
||||||
} from './components/telemetry-dialog/telemetry-dialog';
|
} from './components/telemetry-dialog/telemetry-dialog';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const cache: InMemoryCacheConfig = {
|
const cache: InMemoryCacheConfig = {
|
||||||
typePolicies: {
|
typePolicies: {
|
||||||
@ -181,9 +186,19 @@ const ScrollToTop = () => {
|
|||||||
|
|
||||||
const AppContainer = () => {
|
const AppContainer = () => {
|
||||||
const { config, loading, error } = useEthereumConfig();
|
const { config, loading, error } = useEthereumConfig();
|
||||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } =
|
const {
|
||||||
useEnvironment();
|
VEGA_ENV,
|
||||||
|
VEGA_URL,
|
||||||
|
GIT_COMMIT_HASH,
|
||||||
|
GIT_BRANCH,
|
||||||
|
ETHEREUM_PROVIDER_URL,
|
||||||
|
} = useEnvironment();
|
||||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||||
|
store.dialogOpen,
|
||||||
|
store.setDialogOpen,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ENV.dsn && telemetryOn) {
|
if (ENV.dsn && telemetryOn) {
|
||||||
@ -219,21 +234,29 @@ const AppContainer = () => {
|
|||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<div className="grid min-h-full text-white">
|
<div className="grid min-h-full text-white">
|
||||||
<AsyncRenderer<EthereumConfig | null>
|
<NodeGuard
|
||||||
loading={loading}
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
data={config}
|
failure={
|
||||||
error={error}
|
<AppFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
|
||||||
render={(cnf) =>
|
|
||||||
cnf && (
|
|
||||||
<Web3Container
|
|
||||||
chainId={Number(cnf.chain_id)}
|
|
||||||
providerUrl={ETHEREUM_PROVIDER_URL}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<AsyncRenderer<EthereumConfig | null>
|
||||||
|
loading={loading}
|
||||||
|
data={config}
|
||||||
|
error={error}
|
||||||
|
render={(cnf) =>
|
||||||
|
cnf && (
|
||||||
|
<Web3Container
|
||||||
|
chainId={Number(cnf.chain_id)}
|
||||||
|
providerUrl={ETHEREUM_PROVIDER_URL}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</NodeGuard>
|
||||||
</div>
|
</div>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
|
<NodeSwitcherDialog open={nodeSwitcherOpen} setOpen={setNodeSwitcher} />
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -811,5 +811,6 @@
|
|||||||
"OptOutOfTelemetry": "You can opt out any time via settings",
|
"OptOutOfTelemetry": "You can opt out any time via settings",
|
||||||
"NoThanks": "No thanks",
|
"NoThanks": "No thanks",
|
||||||
"ShareData": "Share data",
|
"ShareData": "Share data",
|
||||||
"ContinueSharingData": "Continue sharing data"
|
"ContinueSharingData": "Continue sharing data",
|
||||||
|
"NodeUnsuitable": "Node: {{url}} is unsuitable"
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
createMarketsDataFragment,
|
createMarketsDataFragment,
|
||||||
assetQuery,
|
assetQuery,
|
||||||
networkParamsQuery,
|
networkParamsQuery,
|
||||||
|
nodeGuardQuery,
|
||||||
} from '@vegaprotocol/mock';
|
} from '@vegaprotocol/mock';
|
||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
@ -158,6 +159,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
|||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
||||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||||
aliasGQLQuery(
|
aliasGQLQuery(
|
||||||
req,
|
req,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
marketsDataQuery,
|
marketsDataQuery,
|
||||||
marketsQuery,
|
marketsQuery,
|
||||||
networkParamsQuery,
|
networkParamsQuery,
|
||||||
|
nodeGuardQuery,
|
||||||
ordersQuery,
|
ordersQuery,
|
||||||
positionsQuery,
|
positionsQuery,
|
||||||
proposalListQuery,
|
proposalListQuery,
|
||||||
@ -82,6 +83,7 @@ const mockTradingPage = (
|
|||||||
) => {
|
) => {
|
||||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
|
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
||||||
aliasGQLQuery(
|
aliasGQLQuery(
|
||||||
req,
|
req,
|
||||||
'Markets',
|
'Markets',
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
import {
|
import {
|
||||||
|
AppFailure,
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
NodeGuard,
|
NodeGuard,
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
@ -9,7 +10,6 @@ import { MaintenancePage } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { AppFailure } from './app-failure';
|
|
||||||
import { Web3Provider } from './web3-provider';
|
import { Web3Provider } from './web3-provider';
|
||||||
|
|
||||||
export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
|
export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './app-failure';
|
|
||||||
export * from './app-loader';
|
export * from './app-loader';
|
||||||
export * from './web3-provider';
|
export * from './web3-provider';
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
|
import {
|
||||||
|
useEnvironment,
|
||||||
|
useNodeHealth,
|
||||||
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||||
import { useGlobalStore } from '../../stores';
|
|
||||||
|
|
||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
return (
|
return (
|
||||||
@ -20,9 +23,7 @@ export const Footer = () => {
|
|||||||
|
|
||||||
export const NodeHealth = () => {
|
export const NodeHealth = () => {
|
||||||
const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment();
|
const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment();
|
||||||
const setNodeSwitcher = useGlobalStore(
|
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||||
(store) => (open: boolean) => store.update({ nodeSwitcherDialog: open })
|
|
||||||
);
|
|
||||||
const { datanodeBlockHeight, text, intent } = useNodeHealth();
|
const { datanodeBlockHeight, text, intent } = useNodeHealth();
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
setNodeSwitcher(true);
|
setNodeSwitcher(true);
|
||||||
|
@ -21,9 +21,10 @@ import {
|
|||||||
NodeSwitcherDialog,
|
NodeSwitcherDialog,
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
useInitializeEnv,
|
useInitializeEnv,
|
||||||
|
useNodeSwitcherStore,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import { useGlobalStore, usePageTitleStore } from '../stores';
|
import { usePageTitleStore } from '../stores';
|
||||||
import { Footer } from '../components/footer';
|
import { Footer } from '../components/footer';
|
||||||
import DialogsContainer from './dialogs-container';
|
import DialogsContainer from './dialogs-container';
|
||||||
import ToastsManager from './toasts-manager';
|
import ToastsManager from './toasts-manager';
|
||||||
@ -115,11 +116,10 @@ function AppBody({ Component }: AppProps) {
|
|||||||
|
|
||||||
function VegaTradingApp(props: AppProps) {
|
function VegaTradingApp(props: AppProps) {
|
||||||
const status = useEnvironment((store) => store.status);
|
const status = useEnvironment((store) => store.status);
|
||||||
const { nodeSwitcherOpen, setNodeSwitcher } = useGlobalStore((store) => ({
|
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||||
nodeSwitcherOpen: store.nodeSwitcherDialog,
|
store.dialogOpen,
|
||||||
setNodeSwitcher: (open: boolean) =>
|
store.setDialogOpen,
|
||||||
store.update({ nodeSwitcherDialog: open }),
|
]);
|
||||||
}));
|
|
||||||
|
|
||||||
useInitializeEnv();
|
useInitializeEnv();
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { create } from 'zustand';
|
|||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
|
|
||||||
interface GlobalStore {
|
interface GlobalStore {
|
||||||
nodeSwitcherDialog: boolean;
|
|
||||||
marketId: string | null;
|
marketId: string | null;
|
||||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||||
shouldDisplayWelcomeDialog: boolean;
|
shouldDisplayWelcomeDialog: boolean;
|
||||||
@ -15,7 +14,6 @@ interface PageTitleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||||
nodeSwitcherDialog: false,
|
|
||||||
marketId: LocalStorage.getItem('marketId') || null,
|
marketId: LocalStorage.getItem('marketId') || null,
|
||||||
shouldDisplayWelcomeDialog: false,
|
shouldDisplayWelcomeDialog: false,
|
||||||
update: (newState) => {
|
update: (newState) => {
|
||||||
|
@ -7,6 +7,7 @@ export * from '../candles-chart/src/lib/chart.mock';
|
|||||||
export * from '../deal-ticket/src/hooks/estimate-order.mock';
|
export * from '../deal-ticket/src/hooks/estimate-order.mock';
|
||||||
export * from '../deposits/src/lib/deposit.mock';
|
export * from '../deposits/src/lib/deposit.mock';
|
||||||
export * from '../environment/src/utils/node.mock';
|
export * from '../environment/src/utils/node.mock';
|
||||||
|
export * from '../environment/src/components/node-guard/node-guard.mock';
|
||||||
export * from '../fills/src/lib/fills.mock';
|
export * from '../fills/src/lib/fills.mock';
|
||||||
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
|
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
|
||||||
export * from '../ledger/src/lib/ledger-entries.mock';
|
export * from '../ledger/src/lib/ledger-entries.mock';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useGlobalStore } from '../../stores';
|
import { useNodeSwitcherStore } from '../../hooks/use-node-switcher-store';
|
||||||
|
|
||||||
export const AppFailure = ({
|
export const AppFailure = ({
|
||||||
title,
|
title,
|
||||||
@ -9,17 +9,13 @@ export const AppFailure = ({
|
|||||||
title: string;
|
title: string;
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { setNodeSwitcher } = useGlobalStore((store) => ({
|
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||||
nodeSwitcherOpen: store.nodeSwitcherDialog,
|
|
||||||
setNodeSwitcher: (open: boolean) =>
|
|
||||||
store.update({ nodeSwitcherDialog: open }),
|
|
||||||
}));
|
|
||||||
const nonIdealWrapperClasses =
|
const nonIdealWrapperClasses =
|
||||||
'h-full min-h-screen flex items-center justify-center';
|
'h-full min-h-screen flex items-center justify-center';
|
||||||
return (
|
return (
|
||||||
<div className={nonIdealWrapperClasses}>
|
<div className={nonIdealWrapperClasses}>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-xl mb-4">{title}</h1>
|
<p className="text-xl mb-4">{title}</p>
|
||||||
{error && <p className="text-sm mb-8">{error}</p>}
|
{error && <p className="text-sm mb-8">{error}</p>}
|
||||||
<Button onClick={() => setNodeSwitcher(true)}>
|
<Button onClick={() => setNodeSwitcher(true)}>
|
||||||
{t('Change node')}
|
{t('Change node')}
|
1
libs/environment/src/components/app-failure/index.ts
Normal file
1
libs/environment/src/components/app-failure/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './app-failure';
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './app-failure';
|
||||||
export * from './network-loader';
|
export * from './network-loader';
|
||||||
export * from './network-switcher';
|
export * from './network-switcher';
|
||||||
export * from './node-guard';
|
export * from './node-guard';
|
||||||
|
11
libs/environment/src/components/node-guard/NodeGuard.graphql
Normal file
11
libs/environment/src/components/node-guard/NodeGuard.graphql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
query NodeGuard {
|
||||||
|
lastBlockHeight
|
||||||
|
networkParametersConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
libs/environment/src/components/node-guard/__generated__/NodeGuard.ts
generated
Normal file
51
libs/environment/src/components/node-guard/__generated__/NodeGuard.ts
generated
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type NodeGuardQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type NodeGuardQuery = { __typename?: 'Query', lastBlockHeight: string, networkParametersConnection: { __typename?: 'NetworkParametersConnection', edges?: Array<{ __typename?: 'NetworkParameterEdge', node: { __typename?: 'NetworkParameter', key: string, value: string } } | null> | null } };
|
||||||
|
|
||||||
|
|
||||||
|
export const NodeGuardDocument = gql`
|
||||||
|
query NodeGuard {
|
||||||
|
lastBlockHeight
|
||||||
|
networkParametersConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useNodeGuardQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useNodeGuardQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useNodeGuardQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useNodeGuardQuery({
|
||||||
|
* variables: {
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useNodeGuardQuery(baseOptions?: Apollo.QueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
|
||||||
|
}
|
||||||
|
export function useNodeGuardLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
|
||||||
|
}
|
||||||
|
export type NodeGuardQueryHookResult = ReturnType<typeof useNodeGuardQuery>;
|
||||||
|
export type NodeGuardLazyQueryHookResult = ReturnType<typeof useNodeGuardLazyQuery>;
|
||||||
|
export type NodeGuardQueryResult = Apollo.QueryResult<NodeGuardQuery, NodeGuardQueryVariables>;
|
@ -0,0 +1,53 @@
|
|||||||
|
import type { NodeGuardQuery } from './__generated__/NodeGuard';
|
||||||
|
import merge from 'lodash/merge';
|
||||||
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
|
export const nodeGuardQuery = (
|
||||||
|
override?: PartialDeep<NodeGuardQuery>
|
||||||
|
): NodeGuardQuery => {
|
||||||
|
const defaultResult: NodeGuardQuery = {
|
||||||
|
lastBlockHeight: '11',
|
||||||
|
networkParametersConnection: {
|
||||||
|
__typename: 'NetworkParametersConnection',
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
__typename: 'NetworkParameterEdge',
|
||||||
|
node: {
|
||||||
|
__typename: 'NetworkParameter' as const,
|
||||||
|
key: 'governance.proposal.market.requiredMajority',
|
||||||
|
value: '0.66',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'NetworkParameterEdge',
|
||||||
|
node: {
|
||||||
|
__typename: 'NetworkParameter' as const,
|
||||||
|
key: 'blockchains.ethereumConfig',
|
||||||
|
value: JSON.stringify({
|
||||||
|
network_id: '3',
|
||||||
|
chain_id: '3',
|
||||||
|
collateral_bridge_contract: {
|
||||||
|
address: '0x7fe27d970bc8Afc3B11Cc8d9737bfB66B1efd799',
|
||||||
|
},
|
||||||
|
multisig_control_contract: {
|
||||||
|
address: '0x6eBc32d66277D94DB8FF2ccF86E36f37F29a52D3',
|
||||||
|
deployment_block_height: 12341882,
|
||||||
|
},
|
||||||
|
staking_bridge_contract: {
|
||||||
|
address: '0xFFb0A0d4806502ceF491aF1141f66669A1Bd0D03',
|
||||||
|
deployment_block_height: 11177313,
|
||||||
|
},
|
||||||
|
token_vesting_contract: {
|
||||||
|
address: '0x680fF88252FA7071CAce7398e77872d54D781d0B',
|
||||||
|
deployment_block_height: 11177353,
|
||||||
|
},
|
||||||
|
confirmations: 3,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return merge(defaultResult, override);
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useStatisticsQuery } from '../../utils/__generated__/Node';
|
import { useNodeGuardQuery } from './__generated__/NodeGuard';
|
||||||
|
|
||||||
export const NodeGuard = ({
|
export const NodeGuard = ({
|
||||||
children,
|
children,
|
||||||
@ -10,14 +10,19 @@ export const NodeGuard = ({
|
|||||||
failure: ReactNode;
|
failure: ReactNode;
|
||||||
skeleton: ReactNode;
|
skeleton: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const { error, loading } = useStatisticsQuery();
|
const { data, error, loading } = useNodeGuardQuery();
|
||||||
const wrapperClasses = 'h-full min-h-screen flex items-center justify-center';
|
const wrapperClasses =
|
||||||
|
'h-full min-h-screen flex items-center justify-center text-black dark:text-white';
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div className={wrapperClasses}>{skeleton}</div>;
|
return <div className={wrapperClasses}>{skeleton}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
// It is possible for nodes to have a functioning datanode, but not return
|
||||||
|
// any net params. The app cannot safely function without net params
|
||||||
|
const netParamEdges = data?.networkParametersConnection.edges;
|
||||||
|
|
||||||
|
if (error || !netParamEdges || !netParamEdges.length) {
|
||||||
return <div className={wrapperClasses}>{failure}</div>;
|
return <div className={wrapperClasses}>{failure}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,29 +124,6 @@ export const RowData = ({
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIsNodeDisabled = () => {
|
|
||||||
if (!isValidUrl(url)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if still waiting or query errored disable node
|
|
||||||
if (loading || error) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subLoading || subError) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are still waiting for a header entry for this
|
|
||||||
// url disable the node
|
|
||||||
if (!headers) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSubFailed = (
|
const getSubFailed = (
|
||||||
subError: ApolloError | undefined,
|
subError: ApolloError | undefined,
|
||||||
subFailed: boolean
|
subFailed: boolean
|
||||||
@ -160,12 +137,7 @@ export const RowData = ({
|
|||||||
<>
|
<>
|
||||||
{id !== CUSTOM_NODE_KEY && (
|
{id !== CUSTOM_NODE_KEY && (
|
||||||
<div className="break-all" data-testid="node">
|
<div className="break-all" data-testid="node">
|
||||||
<Radio
|
<Radio id={`node-url-${id}`} value={url} label={url} />
|
||||||
id={`node-url-${id}`}
|
|
||||||
value={url}
|
|
||||||
label={url}
|
|
||||||
disabled={getIsNodeDisabled()}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<LayoutCell
|
<LayoutCell
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export * from './use-environment';
|
export * from './use-environment';
|
||||||
export * from './use-links';
|
export * from './use-links';
|
||||||
export * from './use-node-health';
|
export * from './use-node-health';
|
||||||
|
export * from './use-node-switcher-store';
|
||||||
export * from './use-vega-releases';
|
export * from './use-vega-releases';
|
||||||
export * from './use-vega-release';
|
export * from './use-vega-release';
|
||||||
|
11
libs/environment/src/hooks/use-node-switcher-store.ts
Normal file
11
libs/environment/src/hooks/use-node-switcher-store.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
export const useNodeSwitcherStore = create<{
|
||||||
|
dialogOpen: boolean;
|
||||||
|
setDialogOpen: (isOpen: boolean) => void;
|
||||||
|
}>()((set) => ({
|
||||||
|
dialogOpen: false,
|
||||||
|
setDialogOpen: (isOpen) => {
|
||||||
|
set({ dialogOpen: isOpen });
|
||||||
|
},
|
||||||
|
}));
|
@ -1,7 +1,11 @@
|
|||||||
import { Fragment, useState } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
|
import { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||||
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
|
import {
|
||||||
|
NodeSwitcherDialog,
|
||||||
|
useEnvironment,
|
||||||
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
|
||||||
const getFeedbackLinks = (gitOriginUrl?: string) =>
|
const getFeedbackLinks = (gitOriginUrl?: string) =>
|
||||||
[
|
[
|
||||||
@ -19,64 +23,59 @@ export const NetworkInfo = () => {
|
|||||||
GITHUB_FEEDBACK_URL,
|
GITHUB_FEEDBACK_URL,
|
||||||
ETHEREUM_PROVIDER_URL,
|
ETHEREUM_PROVIDER_URL,
|
||||||
} = useEnvironment();
|
} = useEnvironment();
|
||||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
|
||||||
|
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||||
const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL);
|
const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div data-testid="git-info">
|
||||||
<div data-testid="git-info">
|
<p data-testid="git-network-data" className="mb-2">
|
||||||
<p data-testid="git-network-data" className="mb-2">
|
{t('Reading network data from')}{' '}
|
||||||
{t('Reading network data from')}{' '}
|
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
{VEGA_URL}
|
||||||
{VEGA_URL}
|
</Lozenge>
|
||||||
</Lozenge>
|
. <Link onClick={() => setNodeSwitcher(true)}>{t('Edit')}</Link>
|
||||||
. <Link onClick={() => setNodeSwitcherOpen(true)}>{t('Edit')}</Link>
|
</p>
|
||||||
|
<p data-testid="git-eth-data" className="mb-2 break-all">
|
||||||
|
{t('Reading Ethereum data from')}{' '}
|
||||||
|
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||||
|
{ETHEREUM_PROVIDER_URL}
|
||||||
|
</Lozenge>
|
||||||
|
.{' '}
|
||||||
|
</p>
|
||||||
|
{GIT_COMMIT_HASH && (
|
||||||
|
<p data-testid="git-commit-hash" className="mb-2">
|
||||||
|
{t('Version/commit hash')}:{' '}
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
GIT_ORIGIN_URL
|
||||||
|
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||||
|
>
|
||||||
|
{GIT_COMMIT_HASH}
|
||||||
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
<p data-testid="git-eth-data" className="mb-2 break-all">
|
)}
|
||||||
{t('Reading Ethereum data from')}{' '}
|
{feedbackLinks.length > 0 && (
|
||||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
<p>
|
||||||
{ETHEREUM_PROVIDER_URL}
|
{t('Known issues and feedback on')}{' '}
|
||||||
</Lozenge>
|
{feedbackLinks.map(({ name, url }, index) => (
|
||||||
.{' '}
|
<Fragment key={index}>
|
||||||
|
<Link key={index} href={url}>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
{feedbackLinks.length > 1 &&
|
||||||
|
index < feedbackLinks.length - 2 &&
|
||||||
|
','}
|
||||||
|
{feedbackLinks.length > 1 &&
|
||||||
|
index === feedbackLinks.length - 1 &&
|
||||||
|
`, ${t('and')} `}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
</p>
|
</p>
|
||||||
{GIT_COMMIT_HASH && (
|
)}
|
||||||
<p data-testid="git-commit-hash" className="mb-2">
|
</div>
|
||||||
{t('Version/commit hash')}:{' '}
|
|
||||||
<Link
|
|
||||||
href={
|
|
||||||
GIT_ORIGIN_URL
|
|
||||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
|
||||||
>
|
|
||||||
{GIT_COMMIT_HASH}
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{feedbackLinks.length > 0 && (
|
|
||||||
<p>
|
|
||||||
{t('Known issues and feedback on')}{' '}
|
|
||||||
{feedbackLinks.map(({ name, url }, index) => (
|
|
||||||
<Fragment key={index}>
|
|
||||||
<Link key={index} href={url}>
|
|
||||||
{name}
|
|
||||||
</Link>
|
|
||||||
{feedbackLinks.length > 1 &&
|
|
||||||
index < feedbackLinks.length - 2 &&
|
|
||||||
','}
|
|
||||||
{feedbackLinks.length > 1 &&
|
|
||||||
index === feedbackLinks.length - 1 &&
|
|
||||||
`, ${t('and')} `}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<NodeSwitcherDialog
|
|
||||||
open={nodeSwitcherOpen}
|
|
||||||
setOpen={setNodeSwitcherOpen}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user