Compare commits
25 Commits
develop
...
feat/mobil
Author | SHA1 | Date | |
---|---|---|---|
|
885dc0b52b | ||
|
58df46687b | ||
|
96a07b6379 | ||
|
619b73212f | ||
|
3e2449fdea | ||
|
0351d3f478 | ||
|
5ab455159c | ||
|
5742759780 | ||
|
94c34a1d76 | ||
|
2c1a3d106c | ||
|
d41e9456bc | ||
|
0bad7e3317 | ||
|
b2d52f6f5c | ||
|
3ae2fe3e1c | ||
|
8eed5f1eb4 | ||
|
84bb32bdd7 | ||
|
87579c0c5f | ||
|
4bb50c0dab | ||
|
96b46d1533 | ||
|
19732d90e0 | ||
|
853d0654d7 | ||
|
b24b630f5f | ||
|
7777420f1f | ||
|
f5406941e7 | ||
|
18a3786c98 |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"short_name": "Mainnet Stats",
|
"short_name": "Explorer VEGA",
|
||||||
"name": "Vega Mainnet statistics",
|
"name": "Vega Protocol - Explorer",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"short_name": "Mainnet Stats",
|
"short_name": "Governance VEGA",
|
||||||
"name": "Vega Mainnet statistics",
|
"name": "Vega Protocol - Governance",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
<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="stylesheet" href="fonts.css" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"short_name": "Mainnet Stats",
|
"name": "Vega Protocol - Trading",
|
||||||
"name": "Vega Mainnet statistics",
|
"short_name": "Console",
|
||||||
|
"description": "Vega Protocol - Trading dApp",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
@ -12,9 +18,5 @@
|
|||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "192x192"
|
"sizes": "192x192"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
<Last24hPriceChange
|
<Last24hPriceChange
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market.decimalPlaces}
|
decimalPlaces={market.decimalPlaces}
|
||||||
|
fallback={<span>-</span>}
|
||||||
/>
|
/>
|
||||||
</HeaderStat>
|
</HeaderStat>
|
||||||
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
|
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
|
||||||
@ -112,7 +113,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
heading={`${t('Funding Rate')} / ${t('Countdown')}`}
|
heading={`${t('Funding Rate')} / ${t('Countdown')}`}
|
||||||
testId="market-funding"
|
testId="market-funding"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between gap-2">
|
<div className="flex gap-2">
|
||||||
<FundingRate marketId={market.id} />
|
<FundingRate marketId={market.id} />
|
||||||
<FundingCountdown marketId={market.id} />
|
<FundingCountdown marketId={market.id} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,6 @@ import { type Market } from '@vegaprotocol/markets';
|
|||||||
// TODO: handle oracle banner
|
// TODO: handle oracle banner
|
||||||
// import { OracleBanner } from '@vegaprotocol/markets';
|
// import { OracleBanner } from '@vegaprotocol/markets';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
@ -12,21 +11,21 @@ import {
|
|||||||
VegaIconNames,
|
VegaIconNames,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
import { MarketBanner } from '../../components/market-banner';
|
|
||||||
import { ErrorBoundary } from '../../components/error-boundary';
|
import { ErrorBoundary } from '../../components/error-boundary';
|
||||||
import { type TradingView } from './trade-views';
|
import { type TradingView } from './trade-views';
|
||||||
import { TradingViews } from './trade-views';
|
import { TradingViews } from './trade-views';
|
||||||
|
|
||||||
interface TradePanelsProps {
|
interface TradePanelsProps {
|
||||||
market: Market;
|
market: Market;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||||
const [view, setView] = useState<TradingView>('chart');
|
const [topView, setTopView] = useState<TradingView>('chart');
|
||||||
const viewCfg = TradingViews[view];
|
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;
|
const Component = TradingViews[view].component;
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
@ -39,12 +38,13 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
|||||||
// so watch out for clashes in props
|
// so watch out for clashes in props
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary feature={view}>
|
<ErrorBoundary feature={view}>
|
||||||
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
|
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenu = () => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const renderMenu = (viewCfg: any) => {
|
||||||
if ('menu' in viewCfg || 'settings' in viewCfg) {
|
if ('menu' in viewCfg || 'settings' in viewCfg) {
|
||||||
return (
|
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">
|
<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 (
|
return (
|
||||||
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
|
<div className="h-full flex flex-col lg:grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||||
<div>
|
<div className="flex flex-col w-full overflow-hidden">
|
||||||
<MarketBanner market={market} />
|
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||||
</div>
|
{['chart', 'orderbook', 'trades', 'liquidity', 'fundingPayments']
|
||||||
<div>{renderMenu()}</div>
|
// filter to control available views for the current market
|
||||||
<div className="h-full relative">
|
// e.g. only perpetuals should get the funding views
|
||||||
<AutoSizer>
|
.filter((_key) => {
|
||||||
{({ width, height }) => (
|
const key = _key as TradingView;
|
||||||
<div style={{ width, height }} className="overflow-auto">
|
const perpOnlyViews = ['funding', 'fundingPayments'];
|
||||||
{renderView()}
|
|
||||||
</div>
|
if (
|
||||||
)}
|
market?.tradableInstrument.instrument.product.__typename ===
|
||||||
</AutoSizer>
|
'Perpetual'
|
||||||
</div>
|
) {
|
||||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
return true;
|
||||||
{Object.keys(TradingViews)
|
}
|
||||||
// filter to control available views for the current market
|
|
||||||
// eg only perps should get the funding views
|
if (perpOnlyViews.includes(key)) {
|
||||||
.filter((_key) => {
|
return false;
|
||||||
const key = _key as TradingView;
|
}
|
||||||
const perpOnlyViews = ['funding', 'fundingPayments'];
|
|
||||||
|
|
||||||
if (
|
|
||||||
market?.tradableInstrument.instrument.product.__typename ===
|
|
||||||
'Perpetual'
|
|
||||||
) {
|
|
||||||
return true;
|
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)) {
|
<div className="flex flex-col w-full grow">
|
||||||
return false;
|
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||||
}
|
{[
|
||||||
|
'positions',
|
||||||
return true;
|
'activeOrders',
|
||||||
})
|
'closedOrders',
|
||||||
.map((_key) => {
|
'rejectedOrders',
|
||||||
|
'orders',
|
||||||
|
'stopOrders',
|
||||||
|
'collateral',
|
||||||
|
'fills',
|
||||||
|
].map((_key) => {
|
||||||
const key = _key as TradingView;
|
const key = _key as TradingView;
|
||||||
const isActive = view === key;
|
const isActive = bottomView === key;
|
||||||
return (
|
return (
|
||||||
<ViewButton
|
<ViewButton
|
||||||
key={key}
|
key={key}
|
||||||
view={key}
|
view={key}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
onClick={() => {
|
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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -157,7 +182,7 @@ const useViewLabel = (view: TradingView) => {
|
|||||||
depth: t('Depth'),
|
depth: t('Depth'),
|
||||||
liquidity: t('Liquidity'),
|
liquidity: t('Liquidity'),
|
||||||
funding: t('Funding'),
|
funding: t('Funding'),
|
||||||
fundingPayments: t('Funding Payments'),
|
fundingPayments: t('Funding'),
|
||||||
orderbook: t('Orderbook'),
|
orderbook: t('Orderbook'),
|
||||||
trades: t('Trades'),
|
trades: t('Trades'),
|
||||||
positions: t('Positions'),
|
positions: t('Positions'),
|
||||||
|
@ -3,7 +3,6 @@ import { Outlet } from 'react-router-dom';
|
|||||||
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
|
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
|
||||||
export const LayoutWithSidebar = ({
|
export const LayoutWithSidebar = ({
|
||||||
header,
|
header,
|
||||||
sidebar,
|
sidebar,
|
||||||
@ -27,10 +26,13 @@ export const LayoutWithSidebar = ({
|
|||||||
<div className={gridClasses}>
|
<div className={gridClasses}>
|
||||||
<div className="col-span-full">{header}</div>
|
<div className="col-span-full">{header}</div>
|
||||||
<main
|
<main
|
||||||
className={classNames('col-start-1 col-end-1 overflow-y-auto', {
|
className={classNames(
|
||||||
'lg:col-end-3': !sidebarOpen,
|
'col-start-1 col-end-1 overflow-hidden lg:overflow-y-auto grow lg:grow-0',
|
||||||
'hidden lg:block lg:col-end-2': sidebarOpen,
|
{
|
||||||
})}
|
'lg:col-end-3': !sidebarOpen,
|
||||||
|
'hidden lg:block lg:col-end-2': sidebarOpen,
|
||||||
|
}
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './market-header';
|
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 './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 MenuState = 'wallet' | 'nav' | null;
|
||||||
type Theme = 'system' | 'yellow';
|
type Theme = 'system' | 'yellow';
|
||||||
|
|
||||||
export const Navbar = ({
|
export const Navbar = ({ theme = 'system' }: { theme?: Theme }) => {
|
||||||
children,
|
|
||||||
theme = 'system',
|
|
||||||
}: {
|
|
||||||
children?: ReactNode;
|
|
||||||
theme?: Theme;
|
|
||||||
}) => {
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const t = useT();
|
const t = useT();
|
||||||
// menu state for small screens
|
// menu state for small screens
|
||||||
@ -75,8 +69,6 @@ export const Navbar = ({
|
|||||||
>
|
>
|
||||||
<VLogo className="w-4" />
|
<VLogo className="w-4" />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
{/* Left section */}
|
|
||||||
<div className="flex items-center lg:hidden">{children}</div>
|
|
||||||
{/* Used to show header in nav on mobile */}
|
{/* Used to show header in nav on mobile */}
|
||||||
<div className="hidden lg:block">
|
<div className="hidden lg:block">
|
||||||
<NavbarMenu onClick={() => setMenu(null)} />
|
<NavbarMenu onClick={() => setMenu(null)} />
|
||||||
|
@ -14,7 +14,7 @@ import './styles.css';
|
|||||||
import { usePageTitleStore } from '../stores';
|
import { usePageTitleStore } from '../stores';
|
||||||
import DialogsContainer from './dialogs-container';
|
import DialogsContainer from './dialogs-container';
|
||||||
import ToastsManager from './toasts-manager';
|
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 { Bootstrapper } from '../components/bootstrapper';
|
||||||
import { AnnouncementBanner } from '../components/banner';
|
import { AnnouncementBanner } from '../components/banner';
|
||||||
import { Navbar } from '../components/navbar';
|
import { Navbar } from '../components/navbar';
|
||||||
@ -25,9 +25,7 @@ import {
|
|||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingBanner } from '../components/viewing-banner';
|
import { ViewingBanner } from '../components/viewing-banner';
|
||||||
import { NavHeader } from '../components/navbar/nav-header';
|
|
||||||
import { Telemetry } from '../components/telemetry';
|
import { Telemetry } from '../components/telemetry';
|
||||||
import { Routes as AppRoutes } from '../lib/links';
|
|
||||||
import { SSRLoader } from './ssr-loader';
|
import { SSRLoader } from './ssr-loader';
|
||||||
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
||||||
import { MaybeConnectEagerly } from './maybe-connect-eagerly';
|
import { MaybeConnectEagerly } from './maybe-connect-eagerly';
|
||||||
@ -73,16 +71,7 @@ function AppBody({ Component }: AppProps) {
|
|||||||
<Title />
|
<Title />
|
||||||
<div className={gridClasses}>
|
<div className={gridClasses}>
|
||||||
<AnnouncementBanner />
|
<AnnouncementBanner />
|
||||||
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'system'}>
|
<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>
|
|
||||||
<div data-testid="banners">
|
<div data-testid="banners">
|
||||||
<ProtocolUpgradeProposalNotification
|
<ProtocolUpgradeProposalNotification
|
||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
|
@ -24,11 +24,14 @@ export default function Document() {
|
|||||||
|
|
||||||
{/* scripts */}
|
{/* scripts */}
|
||||||
<script src="/theme-setter.js" type="text/javascript" async />
|
<script src="/theme-setter.js" type="text/javascript" async />
|
||||||
|
|
||||||
|
{/* manifest */}
|
||||||
|
<link rel="manifest" href="/apps/trading/public/manifest.json" />
|
||||||
</Head>
|
</Head>
|
||||||
<Html>
|
<Html>
|
||||||
<body
|
<body
|
||||||
// Nextjs will set body to display none until js runs. Because the entire app is client rendered
|
// 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
|
// 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
|
// js is downloaded and react takes over rendering
|
||||||
style={{ display: 'block' }}
|
style={{ display: 'block' }}
|
||||||
className="bg-white dark:bg-vega-cdark-900 text-default font-alpha"
|
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 { compact } from 'lodash';
|
||||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
import { LiquidityHeader } from '../components/liquidity-header';
|
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 { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
|
||||||
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
|
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
|
||||||
import { MarketsSidebar } from '../client-pages/markets/markets-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 { CompetitionsTeam } from '../client-pages/competitions/competitions-team';
|
||||||
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
|
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
|
||||||
import { CompetitionsUpdateTeam } from '../client-pages/competitions/competitions-update-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
|
// 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
|
// Using dynamic imports is a workaround for this until pennant is published as ESM
|
||||||
@ -50,6 +51,9 @@ const NotFound = () => {
|
|||||||
|
|
||||||
export const useRouterConfig = (): RouteObject[] => {
|
export const useRouterConfig = (): RouteObject[] => {
|
||||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
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([
|
const routeConfig = compact([
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
@ -151,10 +155,7 @@ export const useRouterConfig = (): RouteObject[] => {
|
|||||||
{
|
{
|
||||||
path: 'markets/*',
|
path: 'markets/*',
|
||||||
element: (
|
element: (
|
||||||
<LayoutWithSidebar
|
<LayoutWithSidebar header={marketHeader} sidebar={<MarketsSidebar />} />
|
||||||
header={<MarketHeader />}
|
|
||||||
sidebar={<MarketsSidebar />}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
children: [
|
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[];
|
initialValue?: string[];
|
||||||
isHeader?: boolean;
|
isHeader?: boolean;
|
||||||
noUpdate?: boolean;
|
noUpdate?: boolean;
|
||||||
|
// render prop for no price change
|
||||||
|
fallback?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Last24hPriceChange = ({
|
export const Last24hPriceChange = ({
|
||||||
marketId,
|
marketId,
|
||||||
decimalPlaces,
|
decimalPlaces,
|
||||||
initialValue,
|
initialValue,
|
||||||
|
fallback,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const { oneDayCandles, error, fiveDaysCandles } = useCandles({
|
const { oneDayCandles, error, fiveDaysCandles } = useCandles({
|
||||||
@ -48,13 +51,13 @@ export const Last24hPriceChange = ({
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span>-</span>
|
<span>{fallback}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error || !isNumeric(decimalPlaces)) {
|
if (error || !isNumeric(decimalPlaces)) {
|
||||||
return <span>-</span>;
|
return <span>{fallback}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const candles = oneDayCandles?.map((c) => c.close) || initialValue || [];
|
const candles = oneDayCandles?.map((c) => c.close) || initialValue || [];
|
||||||
|
Loading…
Reference in New Issue
Block a user