chore(trading): routing and link changes (#4738)

This commit is contained in:
Matthew Russell 2023-09-20 13:28:34 -07:00 committed by GitHub
parent 5db3453ba5
commit 3e30f053ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 381 additions and 344 deletions

View File

@ -0,0 +1,31 @@
import { t } from '@vegaprotocol/i18n';
import { Links } from '../../lib/links';
import classNames from 'classnames';
import { NavLink, Outlet } from 'react-router-dom';
export const Assets = () => {
const linkClasses = ({ isActive }: { isActive: boolean }) => {
return classNames('border-b-2 border-transparent', {
'border-vega-yellow': isActive,
});
};
return (
<div className="max-w-[500px] px-4 mx-auto my-8">
<nav className="flex mb-6 text-lg gap-4">
<NavLink to={Links.DEPOSIT()} className={linkClasses}>
{t('Deposit')}
</NavLink>
<NavLink to={Links.WITHDRAW()} className={linkClasses}>
{t('Withdraw')}
</NavLink>
<NavLink to={Links.TRANSFER()} className={linkClasses}>
{t('Transfer')}
</NavLink>
</nav>
<div className="pt-4 border-t md:p-6 md:border md:rounded-xl border-default">
<Outlet />
</div>
</div>
);
};

View File

@ -0,0 +1 @@
export { Assets } from './assets';

View File

@ -0,0 +1,44 @@
import { t } from '@vegaprotocol/i18n';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { GetStartedCheckList } from '../../components/welcome-dialog';
import {
useGetOnboardingStep,
useOnboardingStore,
OnboardingStep,
} from '../../components/welcome-dialog/use-get-onboarding-step';
import { Links } from '../../lib/links';
import classNames from 'classnames';
export const DepositGetStarted = () => {
const onboardingDismissed = useOnboardingStore((store) => store.dismissed);
const dismiss = useOnboardingStore((store) => store.dismiss);
const step = useGetOnboardingStep();
const wrapperClasses = classNames(
'flex flex-col py-4 px-6 gap-4 rounded',
'bg-vega-blue-300 dark:bg-vega-blue-700',
'border border-vega-blue-350 dark:border-vega-blue-650'
);
// Dont show unless still onboarding
if (onboardingDismissed) {
return null;
}
return (
<div className="pt-6 border-t border-default">
<div className={wrapperClasses}>
<h3 className="text-lg">{t('Get started')}</h3>
<GetStartedCheckList />
{step > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
<TradingAnchorButton
href={Links.HOME()}
onClick={() => dismiss()}
intent={Intent.Info}
>
{t('Start trading')}
</TradingAnchorButton>
)}
</div>
</div>
);
};

View File

@ -0,0 +1,30 @@
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Deposit } from './deposit';
jest.mock('@vegaprotocol/deposits', () => ({
DepositContainer: ({ assetId }: { assetId?: string }) => (
<div data-testid="assetId">{assetId}</div>
),
}));
jest.mock('./deposit-get-started', () => ({
DepositGetStarted: () => <div>DepositGetStarted</div>,
}));
const renderJsx = (route = '/deposit') => {
render(
<MemoryRouter initialEntries={[route]}>
<Deposit />
</MemoryRouter>
);
};
describe('Deposit page', () => {
it('assetId should be passed down', () => {
const assetId = 'foo';
const route = '/deposit?assetId=' + assetId;
renderJsx(route);
expect(screen.getByTestId('assetId')).toHaveTextContent(assetId);
});
});

View File

@ -1,59 +1,14 @@
import { DepositContainer } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { GetStartedCheckList } from '../../components/welcome-dialog';
import {
useGetOnboardingStep,
useOnboardingStore,
OnboardingStep,
} from '../../components/welcome-dialog/use-get-onboarding-step';
import { Links, Routes } from '../../pages/client-router';
import classNames from 'classnames';
import { useSearchParams } from 'react-router-dom';
import { DepositGetStarted } from './deposit-get-started';
export const Deposit = () => {
const [searchParams] = useSearchParams();
const assetId = searchParams.get('assetId') || undefined;
return (
<div className="max-w-[600px] px-4 py-8 mx-auto lg:px-8">
<h1 className="mb-6 text-4xl uppercase xl:text-5xl font-alpha calt">
{t('Deposit')}
</h1>
<div className="flex flex-col gap-6">
<DepositContainer />
<DepositContainer assetId={assetId} />
<DepositGetStarted />
</div>
</div>
);
};
const DepositGetStarted = () => {
const onboardingDismissed = useOnboardingStore((store) => store.dismissed);
const dismiss = useOnboardingStore((store) => store.dismiss);
const step = useGetOnboardingStep();
const wrapperClasses = classNames(
'flex flex-col py-4 px-6 gap-4 rounded',
'bg-vega-blue-300 dark:bg-vega-blue-700',
'border border-vega-blue-350 dark:border-vega-blue-650'
);
// Dont show unless still onboarding
if (onboardingDismissed) {
return null;
}
return (
<div className="pt-6 border-t border-default">
<div className={wrapperClasses}>
<h3 className="text-lg">{t('Get started')}</h3>
<GetStartedCheckList />
{step > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
<TradingAnchorButton
href={Links[Routes.HOME]()}
onClick={() => dismiss()}
intent={Intent.Info}
>
{t('Start trading')}
</TradingAnchorButton>
)}
</div>
</div>
);
};

View File

@ -2,12 +2,11 @@ import { t } from '@vegaprotocol/i18n';
export const Disclaimer = () => {
return (
<div className="py-16 px-8 flex w-full justify-center">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
<>
<h1 className="text-4xl uppercase xl:text-5xl font-alpha calt">
{t('Disclaimer')}
</h1>
<p className="mb-6 mt-10">
<p className="mt-10 mb-6">
{t(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets. The Vega Protocol is an implementation layer (layer one) protocol made of free, public, open-source or source-available software. Use of the Vega Protocol involves various risks, including but not limited to, losses while digital assets are supplied to the Vega Protocol and losses due to the fluctuation of prices of assets.'
)}
@ -42,7 +41,6 @@ export const Disclaimer = () => {
'Additionally, just as you can access email protocols such as SMTP through multiple email clients, you can potentially access the Vega Protocol through many web or mobile interfaces. You are responsible for doing your own diligence on those interfaces to understand the associated risks and any fees.'
)}
</p>
</div>
</div>
</>
);
};

View File

@ -1,3 +1 @@
import { Disclaimer } from './disclaimer';
export default Disclaimer;
export { Disclaimer } from './disclaimer';

View File

@ -1,9 +1,9 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { useGlobalStore } from '../../stores';
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
import { Links } from '../../lib/links';
// The home pages only purpose is to redirect to the users last market,
// the top traded if they are new, or fall back to the list of markets.
@ -15,17 +15,17 @@ export const Home = () => {
useEffect(() => {
if (marketId) {
navigate(Links[Routes.MARKET](marketId), {
navigate(Links.MARKET(marketId), {
replace: true,
});
} else if (data) {
const marketDataId = data[0]?.id;
if (marketDataId) {
navigate(Links[Routes.MARKET](marketDataId), {
navigate(Links.MARKET(marketDataId), {
replace: true,
});
} else {
navigate(Links[Routes.MARKETS]());
navigate(Links.MARKETS());
}
}
}, [marketId, data, navigate]);

View File

@ -1,3 +1 @@
import { Home } from './home';
export default Home;
export { Home } from './home';

View File

@ -1,3 +1 @@
import { Liquidity } from './liquidity';
export default Liquidity;
export { Liquidity } from './liquidity';

View File

@ -1,3 +1 @@
import { MarketPage } from './market';
export default MarketPage;
export { MarketPage as default } from './market';

View File

@ -9,7 +9,7 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
@ -96,12 +96,12 @@ export const MarketPage = () => {
return (
<Splash>
<span className="flex flex-col items-center gap-2">
<p className="text-sm justify-center">
<p className="justify-center text-sm">
{t('This market URL is not available any more.')}
</p>
<p className="text-sm justify-center">
<p className="justify-center text-sm">
{t(`Please choose another market from the`)}{' '}
<ExternalLink onClick={() => navigate(Links[Routes.MARKETS]())}>
<ExternalLink onClick={() => navigate(Links.MARKETS())}>
market list
</ExternalLink>
</p>

View File

@ -1,3 +1 @@
import { MarketsPage } from './markets-page';
export default MarketsPage;
export { MarketsPage } from './markets-page';

View File

@ -10,7 +10,7 @@ import {
import { DApp, EXPLORER_MARKET, useLinks } from '@vegaprotocol/environment';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { Links } from '../../lib/links';
export const MarketActionsDropdown = ({
marketId,
@ -52,7 +52,7 @@ export const MarketActionsDropdown = ({
{parentMarketID && (
<TradingDropdownItem
onClick={() => {
navigate(Links[Routes.MARKET](parentMarketID));
navigate(Links.MARKET(parentMarketID));
}}
>
<VegaIcon name={VegaIconNames.EYE} size={16} />
@ -62,7 +62,7 @@ export const MarketActionsDropdown = ({
{successorMarketID && (
<TradingDropdownItem
onClick={() => {
navigate(Links[Routes.MARKET](successorMarketID));
navigate(Links.MARKET(successorMarketID));
}}
>
<VegaIcon name={VegaIconNames.EYE} size={16} />

View File

@ -1,3 +1 @@
import { Portfolio } from './portfolio';
export default Portfolio;
export { Portfolio as default } from './portfolio';

View File

@ -21,14 +21,6 @@ const renderJsx = (route = '/transfer') => {
};
describe('Transfer page', () => {
it('properly rendered', () => {
renderJsx();
expect(
screen.getByRole('heading', { level: 1, name: 'Transfer' })
).toBeInTheDocument();
expect(screen.getByTestId('assetId')).toBeEmptyDOMElement();
});
it('assetId should be passed down', () => {
const assetId = 'foo';
const route = '/transfer?assetId=' + assetId;

View File

@ -1,5 +1,4 @@
import { useSearchParams } from 'react-router-dom';
import { t } from '@vegaprotocol/i18n';
import { TransferContainer } from '@vegaprotocol/accounts';
import { GetStarted } from '../../components/welcome-dialog';
@ -8,16 +7,9 @@ export const Transfer = () => {
const assetId = searchParams.get('assetId') || undefined;
return (
<div className="flex justify-center w-full px-8 py-16">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<h1 className="text-4xl uppercase xl:text-5xl font-alpha calt">
{t('Transfer')}
</h1>
<div className="mt-10">
<>
<TransferContainer assetId={assetId} />
<GetStarted />
</div>
</div>
</div>
</>
);
};

View File

@ -21,21 +21,10 @@ const renderJsx = (route = '/withdraw') => {
};
describe('Withdraw page', () => {
it('should be properly rendered', () => {
renderJsx();
expect(
screen.getByRole('heading', { level: 1, name: 'Withdraw' })
).toBeInTheDocument();
expect(screen.getByTestId('assetId')).toBeEmptyDOMElement();
});
it('assetId should be passed down', () => {
const assetId = 'foo';
const route = '/withdraw?assetId=' + assetId;
renderJsx(route);
expect(
screen.getByRole('heading', { level: 1, name: 'Withdraw' })
).toBeInTheDocument();
expect(screen.getByTestId('assetId')).toHaveTextContent(assetId);
});
});

View File

@ -1,5 +1,4 @@
import { useSearchParams } from 'react-router-dom';
import { t } from '@vegaprotocol/i18n';
import { GetStarted } from '../../components/welcome-dialog';
import { WithdrawContainer } from '../../components/withdraw-container';
@ -7,16 +6,9 @@ export const Withdraw = () => {
const [searchParams] = useSearchParams();
const assetId = searchParams.get('assetId') || undefined;
return (
<div className="flex justify-center w-full px-8 py-16">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<h1 className="text-4xl uppercase xl:text-5xl font-alpha calt">
{t('Withdraw')}
</h1>
<div className="mt-10">
<>
<WithdrawContainer assetId={assetId} />
<GetStarted />
</div>
</div>
</div>
</>
);
};

View File

@ -0,0 +1,9 @@
import { Outlet } from 'react-router-dom';
export const LayoutCentered = () => {
return (
<div className="mx-auto lg:min-w-[700px] min-w-[300px] max-w-[700px] px-4 lg:px-8 py-8 lg:py-16">
<Outlet />
</div>
);
};

View File

@ -1,7 +1,6 @@
import { Outlet, Routes, Route } from 'react-router-dom';
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
import classNames from 'classnames';
import { Routes as AppRoutes } from '../../pages/client-router';
import { MarketHeader } from '../market-header';
import { LiquidityHeader } from '../liquidity-header';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
@ -23,8 +22,8 @@ export const LayoutWithSidebar = () => {
<div className={gridClasses}>
<div className="col-span-full">
<Routes>
<Route path={AppRoutes.MARKET} element={<MarketHeader />} />
<Route path={AppRoutes.LIQUIDITY} element={<LiquidityHeader />} />
<Route path="markets/:marketId" element={<MarketHeader />} />
<Route path="liquidity/:marketId" element={<LiquidityHeader />} />
</Routes>
</div>
<main

View File

@ -12,7 +12,7 @@ import {
Indicator,
KeyValueTable,
KeyValueTableRow,
Link,
Link as UILink,
} from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
@ -23,6 +23,8 @@ import {
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { DocsLinks } from '@vegaprotocol/environment';
import { Link } from 'react-router-dom';
import { Links } from '../../lib/links';
interface Props {
marketId?: string;
@ -100,6 +102,7 @@ export const MarketLiquiditySupplied = ({
const description = marketId ? (
<section data-testid="liquidity-supplied-tooltip">
<div className="mb-2">
<KeyValueTable>
<KeyValueTableRow>
<span>{t('Supplied stake')}</span>
@ -124,27 +127,26 @@ export const MarketLiquiditySupplied = ({
</span>
</KeyValueTableRow>
</KeyValueTable>
<br />
<div className="flex flex-col gap-2">
<Link
href={`/#/liquidity/${marketId}`}
data-testid="view-liquidity-link"
>
{t('View liquidity provision table')}
</Link>
{DocsLinks && (
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
{t('Learn about providing liquidity')}
</ExternalLink>
)}
</div>
{showMessage && (
<p className="mt-4">
<p className="mb-2">
{t(
'The market has sufficient liquidity but there are not enough priced limit orders in the order book, which are required to deploy liquidity commitment pegged orders.'
)}
</p>
)}
<div className="flex flex-col gap-2">
<Link to={Links.LIQUIDITY(marketId)} data-testid="view-liquidity-link">
<UILink>{t('View liquidity provision table')}</UILink>
</Link>
</div>
{DocsLinks && (
<div>
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
{t('Learn about providing liquidity')}
</ExternalLink>
</div>
)}
</section>
) : (
'-'

View File

@ -1,8 +1,8 @@
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { Routes } from '../../pages/client-router';
import { t } from '@vegaprotocol/i18n';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Links } from '../../lib/links';
// Make sure these match the available __typename properties on product
export const Product = {
@ -55,7 +55,7 @@ export const ProductSelector = ({
);
})}
<Link
to={Routes.MARKETS}
to={Links.MARKETS()}
className="flex items-center ml-auto text-sm gap-2"
title={t('See all markets')}
>

View File

@ -15,7 +15,7 @@ import * as N from '@radix-ui/react-navigation-menu';
import * as D from '@radix-ui/react-dialog';
import { NavLink } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { Links } from '../../lib/links';
import classNames from 'classnames';
import { VegaWalletMenu } from '../vega-wallet';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
@ -142,9 +142,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
// If we have a stored marketId make Trade link go to that market
// otherwise always go to /markets/all
const tradingPath = marketId
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKET]('');
const tradingPath = marketId ? Links.MARKET(marketId) : Links.MARKETS();
return (
<div className="lg:flex lg:h-full gap-3">
@ -171,7 +169,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
<NavbarListDivider />
<NavbarList>
<NavbarItem>
<NavbarLink to={Links[Routes.MARKETS]()} onClick={onClick}>
<NavbarLink to={Links.MARKETS()} onClick={onClick}>
{t('Markets')}
</NavbarLink>
</NavbarItem>
@ -181,7 +179,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
</NavbarLink>
</NavbarItem>
<NavbarItem>
<NavbarLink to={Links[Routes.PORTFOLIO]()} onClick={onClick}>
<NavbarLink to={Links.PORTFOLIO()} onClick={onClick}>
{t('Portfolio')}
</NavbarLink>
</NavbarItem>
@ -209,7 +207,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
</NavbarSubItem>
)}
<NavbarSubItem>
<NavbarLink to={Links[Routes.DISCLAIMER]()} onClick={onClick}>
<NavbarLink to={Links.DISCLAIMER()} onClick={onClick}>
{t('Disclaimer')}
</NavbarLink>
</NavbarSubItem>

View File

@ -11,7 +11,7 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { Routes as AppRoutes } from '../../pages/client-router';
import { Routes as AppRoutes } from '../../lib/links';
jest.mock('../node-health', () => ({
NodeHealthContainer: () => <span data-testid="node-health" />,

View File

@ -13,10 +13,9 @@ import { NodeHealthContainer } from '../node-health';
import { Settings } from '../settings';
import { Tooltip } from '../../components/tooltip';
import { WithdrawContainer } from '../withdraw-container';
import { Routes as AppRoutes } from '../../pages/client-router';
import { GetStarted } from '../welcome-dialog';
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { GetStarted } from '../welcome-dialog';
export enum ViewType {
Order = 'Order',
@ -51,6 +50,28 @@ type SidebarView =
type: ViewType.Settings;
};
const MarketSidebarButtons = () => {
const currentRouteId = useGetCurrentRouteId();
return (
<>
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
);
};
export const Sidebar = () => {
const currentRouteId = useGetCurrentRouteId();
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
@ -81,34 +102,19 @@ export const Sidebar = () => {
{/* buttons for specific routes */}
<Routes>
<Route
path={AppRoutes.MARKETS}
path="markets/all"
// render nothing for markets/all, otherwise markets/:marketId will match with markets/all
element={null}
/>
<Route
// render nothing for portfolio
path={AppRoutes.PORTFOLIO}
path="portfolio"
element={null}
/>
<Route path="markets/:marketId" element={<MarketSidebarButtons />} />
<Route
path={AppRoutes.MARKET}
element={
<>
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
</>
}
path="liquidity/:marketId"
element={<MarketSidebarButtons />}
/>
</Routes>
</nav>

View File

@ -15,7 +15,7 @@ import {
useGetOnboardingStep,
useOnboardingStore,
} from './use-get-onboarding-step';
import { Links, Routes } from '../../pages/client-router';
import { Links, Routes } from '../../lib/links';
import { useGlobalStore } from '../../stores';
import { useSidebar, ViewType } from '../sidebar';
@ -48,17 +48,18 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
return (
<TradingAnchorButton
{...buttonProps}
href={Links[Routes.DEPOSIT]()}
href={Links.DEPOSIT()}
onClick={() => setDialogOpen(false)}
>
{t('Deposit')}
</TradingAnchorButton>
);
} else if (step >= OnboardingStep.ONBOARDING_ORDER_STEP) {
const link = marketId ? Links.MARKET(marketId) : Links.HOME();
return (
<TradingAnchorButton
{...buttonProps}
href={marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]()}
href={link}
onClick={() => {
setViews({ type: ViewType.Order }, Routes.MARKET);
dismiss();

View File

@ -1,12 +1,12 @@
import { t } from '@vegaprotocol/i18n';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Routes } from '../../pages/client-router';
import { Link } from 'react-router-dom';
import { Links } from '../../lib/links';
export const RiskMessage = () => {
return (
<>
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
<div className="p-6 mb-6 bg-vega-light-100 dark:bg-vega-dark-100">
<ul className="list-[square] ml-4">
<li className="mb-1">
{t(
@ -27,7 +27,7 @@ export const RiskMessage = () => {
{t(
'By using the Vega Console, you acknowledge that you have read and understood the'
)}{' '}
<Link className="underline" to={Routes.DISCLAIMER} target="_blank">
<Link className="underline" to={Links.DISCLAIMER()} target="_blank">
<span className="flex items-center gap-1">
<span>{t('Vega Console Disclaimer')}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />

View File

@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { GetStarted } from './get-started';
import { TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
import { Links } from '../../lib/links';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import type { ReactNode } from 'react';
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
@ -15,9 +15,7 @@ export const WelcomeDialogContent = () => {
const { data } = useTopTradedMarkets();
const marketId = data && data[0]?.id;
const link = marketId
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKETS]();
const link = marketId ? Links.MARKET(marketId) : Links.MARKETS();
const lead =
VEGA_ENV === Networks.MAINNET

View File

@ -3,12 +3,10 @@ import { matchRoutes, useLocation } from 'react-router-dom';
export const useGetCurrentRouteId = () => {
const location = useLocation();
const currentRoute = matchRoutes(routerConfig, location);
const lastRoute = currentRoute?.pop();
const matches = matchRoutes(routerConfig, location);
const lastRoute = matches ? matches[matches.length - 1] : undefined;
if (lastRoute) {
const {
route: { id },
} = lastRoute;
const id = lastRoute.route.id;
return id || '';
}
return '';

View File

@ -1,12 +1,12 @@
import { useNavigate } from 'react-router-dom';
import { useCallback } from 'react';
import { Links, Routes } from '../../pages/client-router';
import { Links } from '../../lib/links';
export const useMarketClickHandler = (replace = false) => {
const navigate = useNavigate();
return (selectedId: string, metaKey?: boolean) => {
const link = Links[Routes.MARKET](selectedId);
const link = Links.MARKET(selectedId);
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else {

35
apps/trading/lib/links.ts Normal file
View File

@ -0,0 +1,35 @@
import trimEnd from 'lodash/trimEnd';
// Make all route paths 'absolute' for easier
// href creation
export const Routes = {
HOME: '/',
MARKETS: '/markets/all',
MARKET: '/markets/:marketId',
LIQUIDITY: '/liquidity/:marketId',
PORTFOLIO: '/portfolio',
DISCLAIMER: '/disclaimer',
ASSETS: '/portfolio/assets',
DEPOSIT: '/portfolio/assets/deposit',
WITHDRAW: '/portfolio/assets/withdraw',
TRANSFER: '/portfolio/assets/transfer',
} as const;
type ConsoleLinks = {
[R in keyof typeof Routes]: (...args: string[]) => string;
};
export const Links: ConsoleLinks = {
HOME: () => Routes.HOME,
MARKET: (marketId: string) =>
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
MARKETS: () => Routes.MARKETS,
PORTFOLIO: () => Routes.PORTFOLIO,
LIQUIDITY: (marketId: string) =>
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
DISCLAIMER: () => Routes.DISCLAIMER,
ASSETS: () => Routes.ASSETS,
DEPOSIT: () => Routes.DEPOSIT,
WITHDRAW: () => Routes.WITHDRAW,
TRANSFER: () => Routes.TRANSFER,
};

View File

@ -48,8 +48,8 @@ import {
} from '@vegaprotocol/proposals';
import { ViewingBanner } from '../components/viewing-banner';
import { NavHeader } from '../components/navbar/nav-header';
import { Routes as AppRoutes } from './client-router';
import { Telemetry } from '../components/telemetry';
import { Routes as AppRoutes } from '../lib/links';
const DEFAULT_TITLE = t('Welcome to Vega trading!');

View File

@ -1,126 +1,105 @@
import { Suspense } from 'react';
import type { RouteObject } from 'react-router-dom';
import { Outlet, useRoutes } from 'react-router-dom';
import dynamic from 'next/dynamic';
import { Navigate, Outlet, useRoutes } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import trimEnd from 'lodash/trimEnd';
import { LayoutWithSidebar } from '../components/layouts';
import { LayoutCentered } from '../components/layouts/layout-centered';
import { Home } from '../client-pages/home';
import { Liquidity } from '../client-pages/liquidity';
import { MarketsPage } from '../client-pages/markets';
import { Disclaimer } from '../client-pages/disclaimer';
import { Assets } from '../client-pages/assets';
import { Deposit } from '../client-pages/deposit';
import { Withdraw } from '../client-pages/withdraw';
import { Transfer } from '../client-pages/transfer';
import { Routes } from '../lib/links';
const LazyHome = dynamic(() => import('../client-pages/home'), {
ssr: false,
});
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
// Using dynamic imports is a workaround for this until pennant is published as ESM
const MarketPage = lazy(() => import('../client-pages/market'));
const Portfolio = lazy(() => import('../client-pages/portfolio'));
const LazyLiquidity = dynamic(() => import('../client-pages/liquidity'), {
ssr: false,
});
const LazyMarkets = dynamic(() => import('../client-pages/markets'), {
ssr: false,
});
const LazyMarket = dynamic(() => import('../client-pages/market'), {
ssr: false,
});
const LazyPortfolio = dynamic(() => import('../client-pages/portfolio'), {
ssr: false,
});
const LazyDisclaimer = dynamic(() => import('../client-pages/disclaimer'), {
ssr: false,
});
export enum Routes {
HOME = '/',
MARKET = '/markets/:marketId',
MARKETS = '/markets/all',
PORTFOLIO = '/portfolio',
LIQUIDITY = '/liquidity/:marketId',
DISCLAIMER = '/disclaimer',
DEPOSIT = '/deposit',
WITHDRAW = '/withdraw',
TRANSFER = '/transfer',
}
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
export const Links: ConsoleLinks = {
[Routes.HOME]: () => Routes.HOME,
[Routes.MARKET]: (marketId: string) =>
trimEnd(Routes.MARKET.replace(':marketId', marketId)),
[Routes.MARKETS]: () => Routes.MARKETS,
[Routes.PORTFOLIO]: () => Routes.PORTFOLIO,
[Routes.LIQUIDITY]: (marketId: string) =>
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
[Routes.DISCLAIMER]: () => Routes.DISCLAIMER,
[Routes.DEPOSIT]: () => Routes.DEPOSIT,
[Routes.WITHDRAW]: () => Routes.WITHDRAW,
[Routes.TRANSFER]: () => Routes.TRANSFER,
};
const NotFound = () => (
<Splash>
<p>{t('Page not found')}</p>
</Splash>
);
export const routerConfig: RouteObject[] = [
// Pages that dont use the LayoutWithSidebar must come first
// to ensure they are matched before the catch all route '/*'
{
path: 'disclaimer',
element: <LayoutCentered />,
id: Routes.DISCLAIMER,
children: [{ index: true, element: <Disclaimer /> }],
},
// All other pages will use the sidebar
{
path: '/*',
element: <LayoutWithSidebar />,
children: [
// all pages that require the Layout component (Sidebar)
{
index: true,
element: <LazyHome />,
element: <Home />,
id: Routes.HOME,
},
{
path: 'markets',
element: <Outlet />,
children: [
{
index: true,
element: <Navigate to="all" />,
},
{
path: 'all',
element: <LazyMarkets />,
element: <MarketsPage />,
id: Routes.MARKETS,
},
{
path: ':marketId',
element: <LazyMarket />,
element: <MarketPage />,
id: Routes.MARKET,
},
],
},
{
path: 'portfolio',
element: <LazyPortfolio />,
id: Routes.PORTFOLIO,
},
{
path: 'liquidity',
element: <Outlet />,
children: [
{
path: ':marketId',
element: <LazyLiquidity />,
index: true,
element: <Portfolio />,
id: Routes.PORTFOLIO,
},
{
path: 'assets',
element: <Assets />,
id: Routes.ASSETS,
children: [
{ index: true, element: <Navigate to="deposit" /> },
{ path: 'deposit', element: <Deposit />, id: Routes.DEPOSIT },
{ path: 'withdraw', element: <Withdraw />, id: Routes.WITHDRAW },
{ path: 'transfer', element: <Transfer />, id: Routes.TRANSFER },
],
},
],
},
{
path: 'liquidity/:marketId',
element: <Liquidity />,
id: Routes.LIQUIDITY,
},
],
},
],
},
{
path: Routes.DISCLAIMER,
element: <LazyDisclaimer />,
},
{ path: Routes.DEPOSIT, element: <Deposit /> },
{ path: Routes.WITHDRAW, element: <Withdraw /> },
{ path: Routes.TRANSFER, element: <Transfer /> },
// NotFound page is here so its caught within parent '/*' route
{
path: '*',
element: (
<Splash>
<p>{t('Not found')}</p>
</Splash>
),
element: <NotFound />,
},
],
},
];
@ -129,9 +108,9 @@ export const ClientRouter = () => {
return (
<Suspense
fallback={
<div className="flex items-center justify-center w-full h-full">
<Splash>
<Loader />
</div>
</Splash>
}
>
{routes}

View File

@ -3,8 +3,8 @@ import { useUpdateNetworkParametersToasts } from '@vegaprotocol/proposals';
import { useVegaTransactionToasts } from '@vegaprotocol/web3';
import { useEthereumTransactionToasts } from '@vegaprotocol/web3';
import { useEthereumWithdrawApprovalsToasts } from '@vegaprotocol/web3';
import { Routes } from './client-router';
import { useReadyToWithdrawalToasts } from '@vegaprotocol/withdraws';
import { Links } from '../lib/links';
export const ToastsManager = () => {
useUpdateNetworkParametersToasts();
@ -12,7 +12,7 @@ export const ToastsManager = () => {
useEthereumTransactionToasts();
useEthereumWithdrawApprovalsToasts();
useReadyToWithdrawalToasts({
withdrawalsLink: `${Routes.PORTFOLIO}`,
withdrawalsLink: Links.PORTFOLIO(),
});
const toasts = useToasts((store) => store.toasts);

View File

@ -11,7 +11,7 @@ type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
*/
export const Link = ({ className, children, ...props }: LinkProps) => {
const anchorClassName = classNames(className, {
underline: typeof children === 'string',
'underline underline-offset-4': typeof children === 'string',
'cursor-pointer': props['aria-disabled'] !== true,
'opacity-50 pointer-events-none': props['aria-disabled'] === true,
});