feat(trading): update mobile layout (#5718)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
46e2965fa2
commit
76c07992d3
@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "Mainnet Stats",
|
||||
"name": "Vega Mainnet statistics",
|
||||
"short_name": "Explorer VEGA",
|
||||
"name": "Vega Protocol - Explorer",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "Mainnet Stats",
|
||||
"name": "Vega Mainnet statistics",
|
||||
"short_name": "Governance VEGA",
|
||||
"name": "Vega Protocol - Governance",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Vega Protocol static asseets</title>
|
||||
<title>Vega Protocol static assets</title>
|
||||
<link rel="stylesheet" href="fonts.css" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
|
@ -1,6 +1,12 @@
|
||||
{
|
||||
"short_name": "Mainnet Stats",
|
||||
"name": "Vega Mainnet statistics",
|
||||
"name": "Vega Protocol - Trading",
|
||||
"short_name": "Console",
|
||||
"description": "Vega Protocol - Trading dApp",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
@ -12,9 +18,5 @@
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
]
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
||||
<Last24hPriceChange
|
||||
marketId={market.id}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
fallback={<span>-</span>}
|
||||
/>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
|
||||
@ -112,7 +113,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
||||
heading={`${t('Funding Rate')} / ${t('Countdown')}`}
|
||||
testId="market-funding"
|
||||
>
|
||||
<div className="flex justify-between gap-2">
|
||||
<div className="flex gap-2">
|
||||
<FundingRate marketId={market.id} />
|
||||
<FundingCountdown marketId={market.id} />
|
||||
</div>
|
||||
|
@ -3,7 +3,6 @@ import { type Market } from '@vegaprotocol/markets';
|
||||
// TODO: handle oracle banner
|
||||
// import { OracleBanner } from '@vegaprotocol/markets';
|
||||
import { useState } from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Popover,
|
||||
@ -12,21 +11,21 @@ import {
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { MarketBanner } from '../../components/market-banner';
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { type TradingView } from './trade-views';
|
||||
import { TradingViews } from './trade-views';
|
||||
|
||||
interface TradePanelsProps {
|
||||
market: Market;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}
|
||||
|
||||
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
const [view, setView] = useState<TradingView>('chart');
|
||||
const viewCfg = TradingViews[view];
|
||||
const [topView, setTopView] = useState<TradingView>('chart');
|
||||
const topViewCfg = TradingViews[topView];
|
||||
const [bottomView, setBottomView] = useState<TradingView>('positions');
|
||||
const bottomViewCfg = TradingViews[bottomView];
|
||||
|
||||
const renderView = () => {
|
||||
const renderView = (view: TradingView) => {
|
||||
const Component = TradingViews[view].component;
|
||||
|
||||
if (!Component) {
|
||||
@ -39,12 +38,13 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
// so watch out for clashes in props
|
||||
return (
|
||||
<ErrorBoundary feature={view}>
|
||||
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
|
||||
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const renderMenu = (viewCfg: any) => {
|
||||
if ('menu' in viewCfg || 'settings' in viewCfg) {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||
@ -69,55 +69,80 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div>
|
||||
<MarketBanner market={market} />
|
||||
</div>
|
||||
<div>{renderMenu()}</div>
|
||||
<div className="h-full relative">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }} className="overflow-auto">
|
||||
{renderView()}
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||
{Object.keys(TradingViews)
|
||||
// filter to control available views for the current market
|
||||
// eg only perps should get the funding views
|
||||
.filter((_key) => {
|
||||
const key = _key as TradingView;
|
||||
const perpOnlyViews = ['funding', 'fundingPayments'];
|
||||
<div className="h-full flex flex-col lg:grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div className="flex flex-col w-full overflow-hidden">
|
||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||
{['chart', 'orderbook', 'trades', 'liquidity', 'fundingPayments']
|
||||
// filter to control available views for the current market
|
||||
// e.g. only perpetuals should get the funding views
|
||||
.filter((_key) => {
|
||||
const key = _key as TradingView;
|
||||
const perpOnlyViews = ['funding', 'fundingPayments'];
|
||||
|
||||
if (
|
||||
market?.tradableInstrument.instrument.product.__typename ===
|
||||
'Perpetual'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (perpOnlyViews.includes(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
market?.tradableInstrument.instrument.product.__typename ===
|
||||
'Perpetual'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.map((_key) => {
|
||||
const key = _key as TradingView;
|
||||
const isActive = topView === key;
|
||||
return (
|
||||
<ViewButton
|
||||
key={key}
|
||||
view={key}
|
||||
isActive={isActive}
|
||||
onClick={() => {
|
||||
setTopView(key);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="h-[50vh] lg:h-full relative">
|
||||
<div>{renderMenu(topViewCfg)}</div>
|
||||
<div className="overflow-auto h-full">{renderView(topView)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (perpOnlyViews.includes(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((_key) => {
|
||||
<div className="flex flex-col w-full grow">
|
||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||
{[
|
||||
'positions',
|
||||
'activeOrders',
|
||||
'closedOrders',
|
||||
'rejectedOrders',
|
||||
'orders',
|
||||
'stopOrders',
|
||||
'collateral',
|
||||
'fills',
|
||||
].map((_key) => {
|
||||
const key = _key as TradingView;
|
||||
const isActive = view === key;
|
||||
const isActive = bottomView === key;
|
||||
return (
|
||||
<ViewButton
|
||||
key={key}
|
||||
view={key}
|
||||
isActive={isActive}
|
||||
onClick={() => {
|
||||
setView(key);
|
||||
setBottomView(key);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="relative grow">
|
||||
<div className="flex flex-col">{renderMenu(bottomViewCfg)}</div>
|
||||
<div className="overflow-auto h-full">{renderView(bottomView)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -157,7 +182,7 @@ const useViewLabel = (view: TradingView) => {
|
||||
depth: t('Depth'),
|
||||
liquidity: t('Liquidity'),
|
||||
funding: t('Funding'),
|
||||
fundingPayments: t('Funding Payments'),
|
||||
fundingPayments: t('Funding'),
|
||||
orderbook: t('Orderbook'),
|
||||
trades: t('Trades'),
|
||||
positions: t('Positions'),
|
||||
|
@ -3,7 +3,6 @@ import { Outlet } from 'react-router-dom';
|
||||
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
|
||||
import classNames from 'classnames';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const LayoutWithSidebar = ({
|
||||
header,
|
||||
sidebar,
|
||||
@ -27,10 +26,13 @@ export const LayoutWithSidebar = ({
|
||||
<div className={gridClasses}>
|
||||
<div className="col-span-full">{header}</div>
|
||||
<main
|
||||
className={classNames('col-start-1 col-end-1 overflow-y-auto', {
|
||||
'lg:col-end-3': !sidebarOpen,
|
||||
'hidden lg:block lg:col-end-2': sidebarOpen,
|
||||
})}
|
||||
className={classNames(
|
||||
'col-start-1 col-end-1 overflow-hidden lg:overflow-y-auto grow lg:grow-0',
|
||||
{
|
||||
'lg:col-end-3': !sidebarOpen,
|
||||
'hidden lg:block lg:col-end-2': sidebarOpen,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Outlet />
|
||||
</main>
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './market-header';
|
||||
export * from './mobile-market-header';
|
||||
|
133
apps/trading/components/market-header/mobile-market-header.tsx
Normal file
133
apps/trading/components/market-header/mobile-market-header.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketSelector } from '../market-selector';
|
||||
import {
|
||||
Last24hPriceChange,
|
||||
useMarket,
|
||||
useMarketList,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||
import { useState } from 'react';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import classNames from 'classnames';
|
||||
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
||||
import { MarketMarkPrice } from '../market-mark-price';
|
||||
/**
|
||||
* This is only rendered for the mobile navigation
|
||||
*/
|
||||
export const MobileMarketHeader = () => {
|
||||
const t = useT();
|
||||
const { marketId } = useParams();
|
||||
const { data } = useMarket(marketId);
|
||||
const [openMarket, setOpenMarket] = useState(false);
|
||||
const [openPrice, setOpenPrice] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!marketId) return null;
|
||||
|
||||
return (
|
||||
<div className="pl-3 pr-2 flex justify-between gap-2 h-10 bg-vega-clight-700 dark:bg-vega-cdark-700">
|
||||
<FullScreenPopover
|
||||
open={openMarket}
|
||||
onOpenChange={(x) => {
|
||||
setOpenMarket(x);
|
||||
}}
|
||||
trigger={
|
||||
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-base leading-3 md:text-lg whitespace-nowrap">
|
||||
{data
|
||||
? data.tradableInstrument.instrument.code
|
||||
: t('Select market')}
|
||||
<span
|
||||
className={classNames(
|
||||
'transition-transform ease-in-out duration-300 flex',
|
||||
{
|
||||
'rotate-180': openMarket,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={16} />
|
||||
</span>
|
||||
</h1>
|
||||
}
|
||||
>
|
||||
<MarketSelector
|
||||
currentMarketId={marketId}
|
||||
onSelect={() => setOpenMarket(false)}
|
||||
/>
|
||||
</FullScreenPopover>
|
||||
<FullScreenPopover
|
||||
open={openPrice}
|
||||
onOpenChange={(x) => {
|
||||
setOpenPrice(x);
|
||||
}}
|
||||
trigger={
|
||||
<span className="flex gap-2 items-end md:text-md whitespace-nowrap leading-3">
|
||||
{data && (
|
||||
<>
|
||||
<span className="text-xs">
|
||||
<Last24hPriceChange
|
||||
marketId={data.id}
|
||||
decimalPlaces={data.decimalPlaces}
|
||||
/>
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<MarketMarkPrice
|
||||
marketId={data.id}
|
||||
decimalPlaces={data.decimalPlaces}
|
||||
/>
|
||||
<VegaIcon
|
||||
name={VegaIconNames.CHEVRON_DOWN}
|
||||
size={16}
|
||||
className={classNames(
|
||||
'transition-transform ease-in-out duration-300',
|
||||
{
|
||||
'rotate-180': openPrice,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{data && (
|
||||
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
|
||||
<MarketHeaderStats market={data} />
|
||||
</div>
|
||||
)}
|
||||
</FullScreenPopover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface PopoverProps extends PopoverPrimitive.PopoverProps {
|
||||
trigger: React.ReactNode | string;
|
||||
}
|
||||
|
||||
export const FullScreenPopover = ({
|
||||
trigger,
|
||||
children,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: PopoverProps) => {
|
||||
return (
|
||||
<PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
||||
<PopoverPrimitive.Trigger data-testid="popover-trigger">
|
||||
{trigger}
|
||||
</PopoverPrimitive.Trigger>
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
data-testid="popover-content"
|
||||
className="w-screen bg-vega-clight-800 dark:bg-vega-cdark-800 border-y border-default"
|
||||
sideOffset={0}
|
||||
>
|
||||
{children}
|
||||
</PopoverPrimitive.Content>
|
||||
</PopoverPrimitive.Portal>
|
||||
</PopoverPrimitive.Root>
|
||||
);
|
||||
};
|
@ -1,2 +1 @@
|
||||
export * from './navbar';
|
||||
export * from './nav-header';
|
||||
|
@ -1,81 +0,0 @@
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketSelector } from '../market-selector';
|
||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||
import { useState } from 'react';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import classNames from 'classnames';
|
||||
|
||||
/**
|
||||
* This is only rendered for the mobile navigation
|
||||
*/
|
||||
export const NavHeader = () => {
|
||||
const t = useT();
|
||||
const { marketId } = useParams();
|
||||
const { data } = useMarket(marketId);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!marketId) return null;
|
||||
|
||||
return (
|
||||
<FullScreenPopover
|
||||
open={open}
|
||||
onOpenChange={(x) => {
|
||||
setOpen(x);
|
||||
}}
|
||||
trigger={
|
||||
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-default text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default">
|
||||
{data ? data.tradableInstrument.instrument.code : t('Select market')}
|
||||
<span
|
||||
className={classNames(
|
||||
'transition-transform ease-in-out duration-300',
|
||||
{
|
||||
'rotate-180': open,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
||||
</span>
|
||||
</h1>
|
||||
}
|
||||
>
|
||||
<MarketSelector
|
||||
currentMarketId={marketId}
|
||||
onSelect={() => setOpen(false)}
|
||||
/>
|
||||
</FullScreenPopover>
|
||||
);
|
||||
};
|
||||
|
||||
export interface PopoverProps extends PopoverPrimitive.PopoverProps {
|
||||
trigger: React.ReactNode | string;
|
||||
}
|
||||
|
||||
export const FullScreenPopover = ({
|
||||
trigger,
|
||||
children,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: PopoverProps) => {
|
||||
return (
|
||||
<PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
||||
<PopoverPrimitive.Trigger data-testid="popover-trigger">
|
||||
{trigger}
|
||||
</PopoverPrimitive.Trigger>
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
data-testid="popover-content"
|
||||
className="w-screen bg-vega-clight-800 dark:bg-vega-cdark-800 text-default border border-default"
|
||||
sideOffset={5}
|
||||
>
|
||||
{children}
|
||||
</PopoverPrimitive.Content>
|
||||
</PopoverPrimitive.Portal>
|
||||
</PopoverPrimitive.Root>
|
||||
);
|
||||
};
|
@ -34,13 +34,7 @@ import { supportedLngs } from '../../lib/i18n';
|
||||
type MenuState = 'wallet' | 'nav' | null;
|
||||
type Theme = 'system' | 'yellow';
|
||||
|
||||
export const Navbar = ({
|
||||
children,
|
||||
theme = 'system',
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
theme?: Theme;
|
||||
}) => {
|
||||
export const Navbar = ({ theme = 'system' }: { theme?: Theme }) => {
|
||||
const i18n = useI18n();
|
||||
const t = useT();
|
||||
// menu state for small screens
|
||||
@ -75,8 +69,6 @@ export const Navbar = ({
|
||||
>
|
||||
<VLogo className="w-4" />
|
||||
</NavLink>
|
||||
{/* Left section */}
|
||||
<div className="flex items-center lg:hidden">{children}</div>
|
||||
{/* Used to show header in nav on mobile */}
|
||||
<div className="hidden lg:block">
|
||||
<NavbarMenu onClick={() => setMenu(null)} />
|
||||
|
@ -14,7 +14,7 @@ import './styles.css';
|
||||
import { usePageTitleStore } from '../stores';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
import { HashRouter, useLocation, Route, Routes } from 'react-router-dom';
|
||||
import { HashRouter, useLocation } from 'react-router-dom';
|
||||
import { Bootstrapper } from '../components/bootstrapper';
|
||||
import { AnnouncementBanner } from '../components/banner';
|
||||
import { Navbar } from '../components/navbar';
|
||||
@ -25,9 +25,7 @@ import {
|
||||
ProtocolUpgradeProposalNotification,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ViewingBanner } from '../components/viewing-banner';
|
||||
import { NavHeader } from '../components/navbar/nav-header';
|
||||
import { Telemetry } from '../components/telemetry';
|
||||
import { Routes as AppRoutes } from '../lib/links';
|
||||
import { SSRLoader } from './ssr-loader';
|
||||
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
||||
import { MaybeConnectEagerly } from './maybe-connect-eagerly';
|
||||
@ -73,16 +71,7 @@ function AppBody({ Component }: AppProps) {
|
||||
<Title />
|
||||
<div className={gridClasses}>
|
||||
<AnnouncementBanner />
|
||||
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'system'}>
|
||||
<Routes>
|
||||
<Route
|
||||
path={AppRoutes.MARKETS}
|
||||
// render nothing for markets/all, otherwise markets/:marketId will match with markets/all
|
||||
element={null}
|
||||
/>
|
||||
<Route path={AppRoutes.MARKET} element={<NavHeader />} />
|
||||
</Routes>
|
||||
</Navbar>
|
||||
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'system'} />
|
||||
<div data-testid="banners">
|
||||
<ProtocolUpgradeProposalNotification
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
|
@ -24,11 +24,14 @@ export default function Document() {
|
||||
|
||||
{/* scripts */}
|
||||
<script src="/theme-setter.js" type="text/javascript" async />
|
||||
|
||||
{/* manifest */}
|
||||
<link rel="manifest" href="/apps/trading/public/manifest.json" />
|
||||
</Head>
|
||||
<Html>
|
||||
<body
|
||||
// Nextjs will set body to display none until js runs. Because the entire app is client rendered
|
||||
// and delivered via ipfs we override this to show a server side render loading animation until the
|
||||
// Next.js will set body to display none until js runs. Because the entire app is client rendered
|
||||
// and delivered via IPFS we override this to show a server side render loading animation until the
|
||||
// js is downloaded and react takes over rendering
|
||||
style={{ display: 'block' }}
|
||||
className="bg-white dark:bg-vega-cdark-900 text-default font-alpha"
|
||||
|
@ -23,7 +23,7 @@ import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-bo
|
||||
import { compact } from 'lodash';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { LiquidityHeader } from '../components/liquidity-header';
|
||||
import { MarketHeader } from '../components/market-header';
|
||||
import { MarketHeader, MobileMarketHeader } from '../components/market-header';
|
||||
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
|
||||
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
|
||||
import { MarketsSidebar } from '../client-pages/markets/markets-sidebar';
|
||||
@ -33,6 +33,7 @@ import { CompetitionsTeams } from '../client-pages/competitions/competitions-tea
|
||||
import { CompetitionsTeam } from '../client-pages/competitions/competitions-team';
|
||||
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
|
||||
import { CompetitionsUpdateTeam } from '../client-pages/competitions/competitions-update-team';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
// 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
|
||||
@ -50,6 +51,9 @@ const NotFound = () => {
|
||||
|
||||
export const useRouterConfig = (): RouteObject[] => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
|
||||
const marketHeader = largeScreen ? <MarketHeader /> : <MobileMarketHeader />;
|
||||
const routeConfig = compact([
|
||||
{
|
||||
index: true,
|
||||
@ -151,10 +155,7 @@ export const useRouterConfig = (): RouteObject[] => {
|
||||
{
|
||||
path: 'markets/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<MarketHeader />}
|
||||
sidebar={<MarketsSidebar />}
|
||||
/>
|
||||
<LayoutWithSidebar header={marketHeader} sidebar={<MarketsSidebar />} />
|
||||
),
|
||||
children: [
|
||||
{
|
||||
|
22
apps/trading/public/manifest.json
Normal file
22
apps/trading/public/manifest.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "Vega Protocol - Trading",
|
||||
"short_name": "Console",
|
||||
"description": "Vega Protocol - Trading dApp",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "cover.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
}
|
||||
]
|
||||
}
|
@ -18,12 +18,15 @@ interface Props {
|
||||
initialValue?: string[];
|
||||
isHeader?: boolean;
|
||||
noUpdate?: boolean;
|
||||
// render prop for no price change
|
||||
fallback?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Last24hPriceChange = ({
|
||||
marketId,
|
||||
decimalPlaces,
|
||||
initialValue,
|
||||
fallback,
|
||||
}: Props) => {
|
||||
const t = useT();
|
||||
const { oneDayCandles, error, fiveDaysCandles } = useCandles({
|
||||
@ -48,13 +51,13 @@ export const Last24hPriceChange = ({
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<span>-</span>
|
||||
<span>{fallback}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !isNumeric(decimalPlaces)) {
|
||||
return <span>-</span>;
|
||||
return <span>{fallback}</span>;
|
||||
}
|
||||
|
||||
const candles = oneDayCandles?.map((c) => c.close) || initialValue || [];
|
||||
|
Loading…
Reference in New Issue
Block a user