Merge branch 'master' into task/token-flow-tests

This commit is contained in:
AndyWhiteVega 2022-06-27 17:13:56 +01:00
commit 8d2d1f8a6a
44 changed files with 923 additions and 493 deletions

View File

@ -5,7 +5,10 @@ describe('market list', () => {
});
it('selects menus', () => {
cy.get('.MuiDrawer-root [aria-current]').should('have.text', 'Markets');
cy.get('[aria-label="Sidebar Navigation Menu"] [aria-current]').should(
'have.text',
'Markets'
);
cy.getByTestId('state-trigger').should('have.text', 'Active');
cy.get('[aria-label="Future"]').click();
cy.get('[data-testid="market-assets-menu"] a.active').should(

View File

@ -40,13 +40,7 @@ function App() {
<VegaWalletProvider>
<AppLoader>
<div className="max-h-full min-h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
<DrawerToggle
onToggle={onToggle}
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
className="xs:py-32 xs:px-16"
/>
<div className="flex items-stretch p-16 bg-black text-white-60">
<div className="flex items-center gap-4 ml-auto mr-8">
<VegaWalletConnectButton
setConnectDialog={(open) =>
@ -56,8 +50,17 @@ function App() {
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
<ThemeSwitcher
onToggle={toggleTheme}
className="-my-4"
sunClassName="text-white"
/>
</div>
<DrawerToggle
onToggle={onToggle}
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
className="xs:py-32 xs:px-16 xs:text-white xs:hover:text-blue"
/>
</div>
<Main isMenuOpen={menuOpen} onToggle={onToggle} />

View File

@ -1,14 +0,0 @@
import * as React from 'react';
import type { ReactElement } from 'react';
interface Props {
children: ReactElement | ReactElement[];
}
export const DrawerContainer = ({ children }: Props) => (
<div className="w-full md:w-3/4 grow-1">{children}</div>
);
export const DrawerWrapper = ({ children }: Props) => (
<div className="flex dark:bg-black">{children}</div>
);

View File

@ -0,0 +1,21 @@
import * as React from 'react';
import classNames from 'classnames';
import type { ReactElement } from 'react';
interface Props {
children: ReactElement | ReactElement[];
className?: string;
}
export const DrawerContent = ({ children, className = '' }: Props) => {
const classes = classNames(
'w-full sm:w-full grow-1 p-20 overflow-hidden',
className
);
return (
<main aria-label="Page Content" className={classes}>
{children}
</main>
);
};

View File

@ -1,38 +0,0 @@
import * as React from 'react';
import type { ReactElement } from 'react';
import { Button } from '@vegaprotocol/ui-toolkit';
import type { ButtonProps } from '@vegaprotocol/ui-toolkit';
type MenuItem = {
label: string;
component: ReactElement | ReactElement[];
onClick(): void;
active: boolean;
};
interface Props {
/**
* Menu items passed as an array
*/
menuItems: MenuItem[];
}
export const NavigationDrawerMenu = ({ menuItems }: Props) => {
return (
<ul>
{menuItems.map((item, index) => {
const btnProps = {
variant: item.active ? 'accent' : 'primary',
className: 'w-full mb-8',
onClick: item.onClick,
} as ButtonProps;
return (
<li key={index}>
<Button {...btnProps}>{item.label}</Button>
</li>
);
})}
</ul>
);
};

View File

@ -19,12 +19,10 @@ interface Props {
export const DrawerToggle = ({
onToggle,
variant = DRAWER_TOGGLE_VARIANTS.CLOSE,
className,
className = '',
}: Props) => {
const [iconName, setIconName] = useState(IconNames.MENU);
const classes = classNames('md:hidden', {
[className as string]: className,
});
const classes = classNames('md:hidden', className);
useEffect(() => {
if (variant === DRAWER_TOGGLE_VARIANTS.OPEN) {
@ -34,8 +32,17 @@ export const DrawerToggle = ({
}
}, [variant]);
const ariaLabel = `${
variant === DRAWER_TOGGLE_VARIANTS.OPEN ? 'Open' : 'Close'
} Sidebar Navigation Menu`;
return (
<Button variant="inline-link" className={classes} onClick={onToggle}>
<Button
aria-label={ariaLabel}
variant="inline-link"
className={classes}
onClick={onToggle}
>
<Icon name={iconName as IconName} />
</Button>
);

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import classNames from 'classnames';
import type { ReactElement } from 'react';
interface Props {
children: ReactElement | ReactElement[];
className?: string;
}
export const DrawerWrapper = ({ children, className = '' }: Props) => {
const classes = classNames('flex dark:bg-black md:flex-row', className);
return <div className={classes}>{children}</div>;
};

View File

@ -1,66 +1,60 @@
import * as React from 'react';
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
import classNames from 'classnames';
import type { ReactElement } from 'react';
import { useEffect } from 'react';
import Drawer from '@mui/material/Drawer';
interface Props {
children?: ReactElement | ReactElement[];
isMenuOpen?: boolean;
onToggle(): void;
rtl?: boolean;
outerClasses?: string;
innerClasses?: string;
}
export const NavigationDrawer = ({
isMenuOpen = false,
onToggle,
children,
rtl,
outerClasses = '',
innerClasses = '',
}: Props) => {
const [windowSize, setWindowSize] = React.useState({
width: window.innerWidth,
height: window.innerHeight,
const width = 'w-full md:w-auto md:min-w-[15%] shrink-0';
const position = 'absolute inset-0 h-full z-10 md:static';
const background = 'bg-black/50 dark:bg-white/50';
const flex = 'flex justify-end overflow-hidden';
const joinedClasses = [flex, width, position, background].join(' ');
const outerStyles = classNames(joinedClasses, {
visible: isMenuOpen,
'invisible md:visible': !isMenuOpen,
'flex-row-reverse': !rtl,
[outerClasses]: outerClasses,
});
const mobileScreenWidth = parseInt(theme.screens.md);
const isMobile = windowSize.width <= mobileScreenWidth;
const timeout = React.useRef(0);
const handleResize = () => {
if (timeout.current) {
window.cancelAnimationFrame(timeout.current);
}
const translateClose = rtl ? 'translate-x-full' : '-translate-x-full';
// Setup the new requestAnimationFrame()
timeout.current = window.requestAnimationFrame(function () {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
});
};
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
window.cancelAnimationFrame(timeout.current);
};
}, []);
const drawerRootClasses = {
root: 'w-3/4 md:w-1/4 shrink-0',
paper: 'p-16 w-3/4 md:w-1/4 box-border',
};
const innerStyles = classNames('w-3/4 md:w-full bg-white dark:bg-black', {
'translate-x-0 transition-transform md:transform-none': isMenuOpen,
[`${translateClose} md:transform-none`]: !isMenuOpen,
[innerClasses]: innerClasses,
});
return (
<Drawer
classes={drawerRootClasses}
variant={isMobile ? 'temporary' : 'permanent'}
open={isMobile ? isMenuOpen : true}
onClose={onToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{children}
</Drawer>
<aside aria-label="Sidebar Navigation Menu" className={outerStyles}>
<div
role="presentation"
aria-label="Content Overlay - Click To Close Sidebar Navigation"
className="md:hidden grow h-full"
onClick={onToggle}
/>
<div
role="group"
aria-label="Sidebar Navigation Grouped Content"
className={innerStyles}
>
{children}
</div>
</aside>
);
};

View File

@ -1,4 +1,4 @@
export * from './drawer';
export * from './drawer-menu';
export * from './drawer-toggle';
export * from './drawer-container';
export * from './drawer-content';
export * from './drawer-wrapper';

View File

@ -0,0 +1,51 @@
import type { ReactElement } from 'react';
import { LiquidityIconPath } from './liquidity-icon';
import { MarketIconPath } from './market-icon';
import { TradeIconPath } from './trade-icon';
import { PortfolioIconPath } from './portfolio-icon';
interface IconProps {
name: string;
className: string;
}
interface IconSVGWrapperProps {
className: string;
children: ReactElement | ReactElement[] | null;
}
const getIconPath = (name: string): ReactElement | null => {
switch (name) {
case 'liquidity':
return <LiquidityIconPath />;
case 'market':
return <MarketIconPath />;
case 'trade':
return <TradeIconPath />;
case 'portfolio':
return <PortfolioIconPath />;
default:
return null;
}
};
const IconSVGWrapper = ({ className, children }: IconSVGWrapperProps) => {
return (
<svg
role="presentation"
className={className}
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
>
{children}
</svg>
);
};
export const Icon = ({ name, className }: IconProps) => {
return (
<IconSVGWrapper className={className}>{getIconPath(name)}</IconSVGWrapper>
);
};

View File

@ -0,0 +1 @@
export * from './icon';

View File

@ -0,0 +1,15 @@
export const LiquidityIconPath = () => (
<>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.5268 1.46612L12 1.99999L11.4732 1.46612L12 0.946381L12.5268 1.46612ZM12 3.07542C11.8274 3.25709 11.6185 3.48044 11.3826 3.73943C10.6899 4.49987 9.76793 5.56335 8.84782 6.77789C7.92587 7.99485 7.01715 9.34862 6.34179 10.6903C5.66046 12.0439 5.25 13.3205 5.25 14.4C5.25 19.0485 8.47431 22.15 12 22.15C15.5257 22.15 18.75 19.0485 18.75 14.4C18.75 13.3205 18.3395 12.0439 17.6582 10.6903C16.9829 9.34862 16.0741 7.99485 15.1522 6.77789C14.2321 5.56335 13.3101 4.49987 12.6174 3.73943C12.3815 3.48044 12.1726 3.25709 12 3.07542ZM11.4732 1.46612C11.4734 1.46597 11.4732 1.46612 12 1.99999C12.5268 1.46612 12.5266 1.46597 12.5268 1.46612L12.5336 1.47291L12.5512 1.49036C12.5663 1.5055 12.5884 1.52759 12.6169 1.55634C12.6739 1.61384 12.7566 1.698 12.8615 1.80641C13.071 2.02319 13.3692 2.33722 13.7263 2.72931C14.4399 3.51261 15.3929 4.61164 16.3478 5.8721C17.3009 7.13013 18.2671 8.56386 18.998 10.0159C19.723 11.4561 20.25 12.9795 20.25 14.4C20.25 19.7514 16.4743 23.65 12 23.65C7.52569 23.65 3.75 19.7514 3.75 14.4C3.75 12.9795 4.27704 11.4561 5.00196 10.0159C5.73285 8.56386 6.69913 7.13013 7.65218 5.8721C8.60707 4.61164 9.56014 3.51261 10.2737 2.72931C10.6308 2.33722 10.929 2.02319 11.1385 1.80641C11.2434 1.698 11.3261 1.61384 11.3831 1.55634C11.4116 1.52759 11.4337 1.5055 11.4488 1.49036L11.4664 1.47291L11.4712 1.46817L11.4732 1.46612Z"
/>
<rect x="11.5" y="17.4" width="1" height="1" />
<rect x="12.5" y="16.4" width="1" height="1" />
<rect x="10.5" y="16.4" width="1" height="1" />
<rect x="14.5" y="15.4" width="1" height="1" />
<rect x="13.5" y="11.4" width="1" height="4" />
<rect x="9.5" y="11.4" width="1" height="5" />
</>
);

View File

@ -0,0 +1,17 @@
export const MarketIconPath = () => (
<>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 2H2V20.5V22H3.5H22V20.5H3.5V2Z"
/>
<rect x="5" y="15" width="2" height="2" />
<rect x="7" y="13" width="2" height="2" />
<rect x="9" y="11" width="2" height="2" />
<rect x="11" y="9" width="2" height="2" />
<rect x="13" y="11" width="2" height="2" />
<rect x="15" y="9" width="2" height="2" />
<rect x="17" y="7" width="2" height="2" />
<rect x="19" y="5" width="2" height="2" />
</>
);

View File

@ -0,0 +1,17 @@
export const PortfolioIconPath = () => (
<>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M8.5 3.5H15.5V6H8.5V3.5ZM7 6V3.5C7 2.67157 7.67157 2 8.5 2H15.5C16.3284 2 17 2.67157 17 3.5V6H20.5C21.3284 6 22 6.67157 22 7.5V20.5C22 21.3284 21.3284 22 20.5 22H3.5C2.67157 22 2 21.3284 2 20.5V7.5C2 6.67157 2.67157 6 3.5 6H7ZM15.5 7.5H8.5H3.5L3.5 20.5H20.5V7.5H15.5Z"
/>
<rect x="4" y="8" width="2" height="2" />
<rect x="6" y="10" width="2" height="2" />
<rect x="18" y="8" width="2" height="2" />
<rect x="16" y="10" width="2" height="2" />
<rect x="14" y="12" width="2" height="2" />
<rect x="8" y="12" width="2" height="2" />
<rect x="10" y="14" width="2" height="2" />
<rect x="12" y="14" width="2" height="2" />
</>
);

View File

@ -0,0 +1,14 @@
export const TradeIconPath = () => (
<>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M19.3254 7.28784L14.8547 2.93099L15.7621 1.99999L21.4056 7.49983L21.8833 7.96534L21.4056 8.43085L15.7621 13.9307L14.8547 12.9997L19.3819 8.58784L1.99999 8.58784L1.99999 7.28784L19.3254 7.28784Z"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.55785 16.6429L9.02855 20.9997L8.12124 21.9307L2.47767 16.4309L2 15.9654L2.47767 15.4999L8.12124 10L9.02855 10.931L4.50141 15.3429L21.8833 15.3429V16.6429L4.55785 16.6429Z"
/>
</>
);

View File

@ -1,62 +1,41 @@
import React from 'react';
import { useRoutes, NavLink } from 'react-router-dom';
import { useRoutes } from 'react-router-dom';
import {
NavigationDrawer,
DrawerWrapper,
DrawerContainer,
DrawerContent,
DrawerToggle,
DRAWER_TOGGLE_VARIANTS,
} from '../drawer';
import { Nav, TabBar } from '../nav';
import { routerConfig } from '../../routes/router-config';
export interface RouteChildProps {
name: string;
}
export const AppRouter = () => {
const routes = useRoutes(routerConfig);
return <main className="p-20 overflow-hidden">{routes}</main>;
};
export const Menu = () => (
<nav>
{routerConfig.map((r) => (
<NavLink
key={r.name}
to={r.path}
className={({ isActive }) =>
`text-h5 block mb-8 px-8 hover:bg-vega-yellow hover:text-black ${
isActive && 'bg-vega-yellow text-black'
}`
}
>
{r.text}
</NavLink>
))}
</nav>
);
export const AppRouter = () => useRoutes(routerConfig);
interface Props {
onToggle(): void;
isMenuOpen: boolean;
}
export const Main = ({ onToggle, isMenuOpen }: Props) => {
return (
<DrawerWrapper>
<NavigationDrawer onToggle={onToggle} isMenuOpen={isMenuOpen}>
<NavigationDrawer rtl onToggle={onToggle} isMenuOpen={isMenuOpen}>
<DrawerToggle
onToggle={onToggle}
variant={DRAWER_TOGGLE_VARIANTS.CLOSE}
className="self-end p-16"
className="p-16"
/>
<Menu />
<Nav className="hidden md:block my-20 h-full" />
</NavigationDrawer>
<DrawerContainer>
<DrawerContent>
<AppRouter />
</DrawerContainer>
<TabBar className="md:hidden" />
</DrawerContent>
</DrawerWrapper>
);
};

View File

@ -0,0 +1,2 @@
export * from './nav';
export * from './tab-bar';

View File

@ -0,0 +1,16 @@
import React from 'react';
import { Icon } from '../icons';
interface NavItemProps {
iconName: string;
label?: string;
}
export const NavItem = ({ iconName, label }: NavItemProps) => {
return (
<div className="flex flex-col md:flex-row items-center justify-start cursor-pointer relative">
<Icon name={iconName} className="mr-8" />
<span>{label}</span>
</div>
);
};

View File

@ -0,0 +1,30 @@
import { routerConfig } from '../../routes';
import { NavLink } from 'react-router-dom';
import { NavItem } from './nav-item';
import React from 'react';
interface NavProps {
className?: string;
tabs?: boolean;
}
export const Nav = ({ className, tabs = false }: NavProps) => {
return (
<nav role={tabs ? 'tablist' : 'menu'} className={className}>
{routerConfig.map((r) => (
<NavLink
role={tabs ? 'tab' : 'menuitem'}
key={r.name}
to={r.path}
className={({ isActive }) =>
`text-h5 block md:mb-40 px-40 md:text-black md:dark:text-white ${
isActive && 'text-white md:text-blue md:dark:text-blue'
}`
}
>
<NavItem iconName={r.icon} label={r.text} />
</NavLink>
))}
</nav>
);
};

View File

@ -0,0 +1,15 @@
import { Nav } from './nav';
import React from 'react';
interface TabBarProps {
className?: string;
}
export const TabBar = ({ className }: TabBarProps) => (
<div role="group" aria-label="Tab Bar Navigation Menu" className={className}>
<div role="presentation" className="py-[42px]" />
<div className="md:hidden fixed bottom-0 left-0 right-0 bg-black py-16">
<Nav tabs className="flex justify-evenly items-center" />
</div>
</div>
);

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import { makeDataProvider } from '@vegaprotocol/react-helpers';
import type {
@ -87,15 +88,16 @@ export const FILTERS_QUERY = gql`
`;
const update = (
draft: SimpleMarkets_markets[],
data: SimpleMarkets_markets[],
delta: SimpleMarketDataSub_marketData
) => {
const index = draft.findIndex((m) => m.id === delta.market.id);
if (index !== -1) {
draft[index].data = delta;
}
// @TODO - else push new market to draft
};
) =>
produce(data, (draft) => {
const index = draft.findIndex((m) => m.id === delta.market.id);
if (index !== -1) {
draft[index].data = delta;
}
// @TODO - else push new market to draft
});
const getData = (responseData: SimpleMarkets) => responseData.markets;
const getDelta = (

View File

@ -4,20 +4,12 @@ import { SimpleMarketList } from '../components/simple-market-list';
import { Portfolio } from '../components/portfolio';
export const ROUTES = {
HOME: '/',
MARKETS: 'markets',
TRADING: 'trading',
LIQUIDITY: 'liquidity',
PORTFOLIO: 'portfolio',
};
export const routerConfig = [
{
path: ROUTES.HOME,
name: 'Home',
text: t('Home'),
element: <div>Home</div>,
},
{
path: ROUTES.MARKETS,
children: [
@ -36,6 +28,7 @@ export const routerConfig = [
name: 'Markets',
text: t('Markets'),
element: <SimpleMarketList />,
icon: 'market',
},
{
path: ROUTES.TRADING,
@ -48,17 +41,13 @@ export const routerConfig = [
element: <DealTicketContainer />,
},
],
},
{
path: ROUTES.LIQUIDITY,
name: 'Liquidity',
text: t('Liquidity'),
element: <div>Liquidity</div>,
icon: 'trade',
},
{
path: ROUTES.PORTFOLIO,
name: 'Portfolio',
text: t('Portfolio'),
element: <Portfolio />,
icon: 'portfolio',
},
];

View File

@ -38,7 +38,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "125135.491715841699705585",
"locked_amount": "124661.013711767544930765",
"deposits": [
{
"amount": "129999.45",
@ -488,7 +488,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "65968.462097822203561422",
"locked_amount": "65503.04337545080622283",
"deposits": [
{
"amount": "97499.58",
@ -521,7 +521,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "0",
"locked_amount": "90167.49870786346458279454272",
"locked_amount": "89531.35166557213976323285608",
"deposits": [
{
"amount": "135173.4239508",
@ -554,7 +554,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "27751.792790148093061242",
"locked_amount": "27555.99917099787937361",
"deposits": [
{
"amount": "32499.86",
@ -587,7 +587,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "9032.939256170343795749",
"locked_amount": "8969.210333073256934278",
"deposits": [
{
"amount": "10833.29",
@ -675,7 +675,7 @@
"tranche_end": "2022-11-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "0",
"locked_amount": "15712.88213315217375",
"locked_amount": "15468.3027626811615",
"deposits": [
{
"amount": "15000",
@ -761,7 +761,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "0",
"locked_amount": "1815010.058055673609877812",
"locked_amount": "1804379.69836047439128314",
"deposits": [
{
"amount": "1852091.69",
@ -1776,8 +1776,8 @@
"tranche_start": "2021-09-05T00:00:00.000Z",
"tranche_end": "2022-09-30T00:00:00.000Z",
"total_added": "60916.66666633337",
"total_removed": "18323.723696937179372649",
"locked_amount": "15072.4168891625501902368233482636",
"total_removed": "18705.279504739679372649",
"locked_amount": "14760.0056484630890178248654459722",
"deposits": [
{
"amount": "2833.333333",
@ -1876,6 +1876,11 @@
}
],
"withdrawals": [
{
"amount": "381.5558078025",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
"tx": "0xdf5387ab07596bf2a4a608ea38d3f98f8020941f4fa7ad88f1ccd223669ac2c7"
},
{
"amount": "327.532400005",
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
@ -2591,6 +2596,12 @@
}
],
"withdrawals": [
{
"amount": "381.5558078025",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
"tranche_id": 13,
"tx": "0xdf5387ab07596bf2a4a608ea38d3f98f8020941f4fa7ad88f1ccd223669ac2c7"
},
{
"amount": "490.9767850775",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
@ -2635,8 +2646,8 @@
}
],
"total_tokens": "4250",
"withdrawn_tokens": "2838.102475055",
"remaining_tokens": "1411.897524945"
"withdrawn_tokens": "3219.6582828575",
"remaining_tokens": "1030.3417171425"
},
{
"address": "0xDFaF6D0a0102ea5e4688F95Eb22Dc353751a7563",
@ -5216,8 +5227,8 @@
"tranche_start": "2021-09-03T00:00:00.000Z",
"tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "19455.000000000000000003",
"total_removed": "5052.45813105178",
"locked_amount": "3704.25642979452060480057120376712328768",
"total_removed": "5056.88782409978",
"locked_amount": "3597.64752092846370375055476445966514475",
"deposits": [
{
"amount": "75",
@ -8271,6 +8282,11 @@
}
],
"withdrawals": [
{
"amount": "4.429693048",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
"tx": "0xfbca378f6263977884e6eebc653c8d68f39b6c8eb6ce2f6668767ed63bfcc55b"
},
{
"amount": "8.25227042",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
@ -14019,6 +14035,12 @@
}
],
"withdrawals": [
{
"amount": "4.429693048",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
"tranche_id": 11,
"tx": "0xfbca378f6263977884e6eebc653c8d68f39b6c8eb6ce2f6668767ed63bfcc55b"
},
{
"amount": "8.25227042",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
@ -14075,8 +14097,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "157.871905124",
"remaining_tokens": "42.128094876"
"withdrawn_tokens": "162.301598172",
"remaining_tokens": "37.698401828"
},
{
"address": "0x1775cc97E5c05Fde8b571ef75CA52d0A9ff19025",
@ -14101,7 +14123,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "74162.9780761646031",
"locked_amount": "2813539.86457541017334277964",
"locked_amount": "2797204.7028220379638062054",
"deposits": [
{
"amount": "1998.95815",
@ -14813,8 +14835,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15788853.065470999700000001",
"total_removed": "6989.59530997724295",
"locked_amount": "15198109.6225936964233990941947241879805353",
"total_removed": "7926.7627792759659",
"locked_amount": "15140482.7365636033722263648641426217383077",
"deposits": [
{
"amount": "16249.93",
@ -15323,6 +15345,21 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xf2208f6e0a6d6b7f5e52e128632ac52b8473b408421318b9a28460aa19feca9c"
},
{
"amount": "358.037173900154925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x2a92ef6c2eb5779e6219058a74210fcd461484d42d623dd06dcb9886683d7b50"
},
{
"amount": "226.8137106757935",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xcb5d0a1e6fdae5bed77fe2f8a29df14985d9f21d0e681416c618104b530fab36"
},
{
"amount": "352.316584722774525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{
"amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15427,6 +15464,24 @@
"tranche_id": 2,
"tx": "0xf2208f6e0a6d6b7f5e52e128632ac52b8473b408421318b9a28460aa19feca9c"
},
{
"amount": "358.037173900154925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x2a92ef6c2eb5779e6219058a74210fcd461484d42d623dd06dcb9886683d7b50"
},
{
"amount": "226.8137106757935",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xcb5d0a1e6fdae5bed77fe2f8a29df14985d9f21d0e681416c618104b530fab36"
},
{
"amount": "352.316584722774525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{
"amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15471,8 +15526,8 @@
}
],
"total_tokens": "194999.1675",
"withdrawn_tokens": "6989.59530997724295",
"remaining_tokens": "188009.57219002275705"
"withdrawn_tokens": "7926.7627792759659",
"remaining_tokens": "187072.4047207240341"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@ -16876,8 +16931,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "2111906.584458928859990382",
"locked_amount": "8381555.21023446802429182215306625",
"total_removed": "2113641.840890679071831632",
"locked_amount": "8328080.66510881884167481291924447",
"deposits": [
{
"amount": "129284.449",
@ -17111,6 +17166,21 @@
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0xd950c34bf5f503998bddc8f9fdeb7cdc4ae1e76c5e9b1564795c306c2e8cad63"
},
{
"amount": "657.674387973356598",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xd0f13de38f21ba54433885d7e3e3db7438b9bcc92dbd52a75e0b7f772018ab1c"
},
{
"amount": "424.7645385693677305",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x77d249279d8ea7554d19c557311687d57884b5e08537ac9574897ca58a13d880"
},
{
"amount": "652.81750520748751275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{
"amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -18711,6 +18781,24 @@
"tranche_id": 3,
"tx": "0xcb8ecc71a9024bab7454a33f6adf7bfcd0f25b3dc1919f92ca362a25b71f2a4d"
},
{
"amount": "657.674387973356598",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xd0f13de38f21ba54433885d7e3e3db7438b9bcc92dbd52a75e0b7f772018ab1c"
},
{
"amount": "424.7645385693677305",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x77d249279d8ea7554d19c557311687d57884b5e08537ac9574897ca58a13d880"
},
{
"amount": "652.81750520748751275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{
"amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -19949,8 +20037,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "152357.71935946585119525",
"remaining_tokens": "206765.75021553414880475"
"withdrawn_tokens": "154092.9757912160630365",
"remaining_tokens": "205030.4937837839369635"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@ -20947,7 +21035,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "1390546.591547348229906227",
"locked_amount": "2994699.75124732873280622934348863",
"locked_amount": "2973571.63505682836710488797665665",
"deposits": [
{
"amount": "552496.6455",
@ -22069,8 +22157,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "11.163032724",
"locked_amount": "445821.47442149508818133870319632",
"total_removed": "34.173053016",
"locked_amount": "443233.07466590775804275636428212",
"deposits": [
{
"amount": "3000",
@ -28693,6 +28781,11 @@
"amount": "11.163032724",
"user": "0xF5037DDA4A660d67560200f45380FF8364e35540",
"tx": "0x0db96c68606a35c59786e3b1ff4f8e939a56af133709dd154718eb03138fe227"
},
{
"amount": "23.010020292",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tx": "0x8daf320262d0384cf5f9c290cc33721587beabd5e93026b3e9b76dc3fcd6659c"
}
],
"users": [
@ -47207,10 +47300,17 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "23.010020292",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tranche_id": 5,
"tx": "0x8daf320262d0384cf5f9c290cc33721587beabd5e93026b3e9b76dc3fcd6659c"
}
],
"total_tokens": "400",
"withdrawn_tokens": "0",
"remaining_tokens": "400"
"withdrawn_tokens": "23.010020292",
"remaining_tokens": "376.989979708"
},
{
"address": "0xF5037DDA4A660d67560200f45380FF8364e35540",
@ -47736,7 +47836,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42",
"total_removed": "31035.4825162206377",
"total_removed": "32094.1716569431377",
"locked_amount": "0",
"deposits": [
{
@ -52011,6 +52111,31 @@
"user": "0x4796314cC5bDa80Ee2E8b119004b1fc72Cfb1783",
"tx": "0xbfdbd5e3712968f2726e8dba1f226944cfa637f8738f25c3a5b12a1c6a8e7197"
},
{
"amount": "250",
"user": "0xC26B3b40DC383d97B69Eedcd4E78a9e4eEb73499",
"tx": "0x2c9f57d5e697c29b67034cea8738939ec13b20b5a7d7fcb995fe83f16b4abf75"
},
{
"amount": "250",
"user": "0x9f6b5A46593b991dC78a0e1907a07517A1F80CC2",
"tx": "0x55b21517cf5fd148e596d9e4b63964ab1df82534fd76b6d9dc9d7cfd00971da0"
},
{
"amount": "250",
"user": "0x696971413b8Ae5342277AfeC16591271648B4FfF",
"tx": "0xa55b22bf465927c195b424ad7ea893c1c497da34290d5261be205c760fc11cf5"
},
{
"amount": "250",
"user": "0x8FC36B7695965Bc39BCB8a3679529f1825e472aE",
"tx": "0x7072667bf81811acda07177c1ace2edcfe19ce68711fa349ad94c1f8aa3d0b25"
},
{
"amount": "58.6891407225",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
},
{
"amount": "183.6335597275",
"user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786",
@ -58532,6 +58657,12 @@
}
],
"withdrawals": [
{
"amount": "58.6891407225",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
"tranche_id": 6,
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
},
{
"amount": "65.928596865",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
@ -58546,8 +58677,8 @@
}
],
"total_tokens": "250",
"withdrawn_tokens": "191.3108592775",
"remaining_tokens": "58.6891407225"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x243e23c83135cA0fed2F9f5dF9068dE644929433",
@ -61925,10 +62056,17 @@
"tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0x9f6b5A46593b991dC78a0e1907a07517A1F80CC2",
"tranche_id": 6,
"tx": "0x55b21517cf5fd148e596d9e4b63964ab1df82534fd76b6d9dc9d7cfd00971da0"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x758E9AA35F2feA08aEc1613A0F0d9Ebe4d374152",
@ -61980,10 +62118,17 @@
"tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0x8FC36B7695965Bc39BCB8a3679529f1825e472aE",
"tranche_id": 6,
"tx": "0x7072667bf81811acda07177c1ace2edcfe19ce68711fa349ad94c1f8aa3d0b25"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x8186CDe8f7A2f95b50f847e2a3301c28bB61Fa8A",
@ -63994,10 +64139,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0x696971413b8Ae5342277AfeC16591271648B4FfF",
"tranche_id": 6,
"tx": "0xa55b22bf465927c195b424ad7ea893c1c497da34290d5261be205c760fc11cf5"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xcbEca0abcc675A0F977c4Eb9a45bB4153C0246C3",
@ -64031,10 +64183,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xC26B3b40DC383d97B69Eedcd4E78a9e4eEb73499",
"tranche_id": 6,
"tx": "0x2c9f57d5e697c29b67034cea8738939ec13b20b5a7d7fcb995fe83f16b4abf75"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xd866082E19c9E36bcA4fA649d530166303B656f2",

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "42.21141552511416",
"locked_amount": "41.663429096905125",
"deposits": [
{
"amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100",
"total_removed": "673.04388635",
"locked_amount": "327.08752536783356",
"locked_amount": "321.05967465753423",
"deposits": [
{
"amount": "1000",

View File

@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "300.32585077371895640029735232749873164",
"locked_amount": "294.7911558219177930002918724315068493",
"deposits": [
{
"amount": "1000",

View File

@ -24,6 +24,7 @@ export const ProposalsContainer = () => {
const { t } = useTranslation();
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
pollInterval: 5000,
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
});
const proposals = React.useMemo(() => {

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import type {
Accounts,
@ -52,17 +53,18 @@ export const getId = (
) => `${data.type}-${data.asset.symbol}-${data.market?.id ?? 'null'}`;
const update = (
draft: Accounts_party_accounts[],
data: Accounts_party_accounts[],
delta: AccountSubscribe_accounts
) => {
const id = getId(delta);
const index = draft.findIndex((a) => getId(a) === id);
if (index !== -1) {
draft[index] = delta;
} else {
draft.push(delta);
}
};
) =>
produce(data, (draft) => {
const id = getId(delta);
const index = draft.findIndex((a) => getId(a) === id);
if (index !== -1) {
draft[index] = delta;
} else {
draft.push(delta);
}
});
const getData = (responseData: Accounts): Accounts_party_accounts[] | null =>
responseData.party ? responseData.party.accounts : null;
const getDelta = (

View File

@ -1,5 +1,4 @@
import { DepthChart } from 'pennant';
import { produce } from 'immer';
import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import {
@ -92,28 +91,31 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
if (!dataRef.current) {
return false;
}
dataRef.current = produce(dataRef.current, (draft) => {
if (delta.buy) {
draft.data.buy = updateLevels(
draft.data.buy,
delta.buy,
decimalPlacesRef.current
);
}
if (delta.sell) {
draft.data.sell = updateLevels(
draft.data.sell,
delta.sell,
decimalPlacesRef.current
);
}
draft.midPrice = delta.market.data?.staticMidPrice
dataRef.current = {
...dataRef.current,
midPrice: delta.market.data?.staticMidPrice
? formatMidPrice(
delta.market.data?.staticMidPrice,
decimalPlacesRef.current
)
: undefined;
});
: undefined,
data: {
buy: delta.buy
? updateLevels(
dataRef.current.data.buy,
delta.buy,
decimalPlacesRef.current
)
: dataRef.current.data.buy,
sell: delta.sell
? updateLevels(
dataRef.current.data.sell,
delta.sell,
decimalPlacesRef.current
)
: dataRef.current.data.sell,
},
};
setDepthDataThrottledRef.current(dataRef.current);
return true;
},

View File

@ -86,27 +86,31 @@ const sequenceNumbers: Record<string, number> = {};
const update: Update<
MarketDepth_market,
MarketDepthSubscription_marketDepthUpdate
> = (draft, delta, reload) => {
if (delta.market.id !== draft.id) {
return;
> = (data, delta, reload) => {
if (delta.market.id !== data.id) {
return data;
}
const sequenceNumber = Number(delta.sequenceNumber);
if (sequenceNumber <= sequenceNumbers[delta.market.id]) {
return;
return data;
}
/*
if (sequenceNumber - 1 !== sequenceNumbers[delta.market.id]) {
sequenceNumbers[delta.market.id] = 0;
reload();
return;
}
*/
sequenceNumbers[delta.market.id] = sequenceNumber;
Object.assign(draft.data, delta.market.data);
const updatedData = { ...data };
data.data = delta.market.data;
if (delta.buy) {
draft.depth.buy = updateLevels(draft.depth.buy ?? [], delta.buy);
updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy);
}
if (delta.sell) {
draft.depth.sell = updateLevels(draft.depth.sell ?? [], delta.sell);
updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell);
}
return updatedData;
};
const getData = (responseData: MarketDepth) => {

View File

@ -55,10 +55,8 @@ describe('compactRows', () => {
'1097': 3,
'1098': 2,
'1099': 1,
'1100': 0,
});
expect(orderbookRows[orderbookRows.length - 1].bidByLevel).toEqual({
'901': 0,
'902': 1,
'903': 2,
'904': 3,
@ -81,7 +79,7 @@ describe('compactRows', () => {
});
describe('updateLevels', () => {
const levels: MarketDepth_market_depth_sell[] = new Array(10)
let levels: MarketDepth_market_depth_sell[] = new Array(10)
.fill(null)
.map((n, i) => ({
__typename: 'PriceLevel',
@ -96,9 +94,9 @@ describe('updateLevels', () => {
volume: '0',
numberOfOrders: '0',
};
updateLevels(levels, [removeFirstRow]);
levels = updateLevels(levels, [removeFirstRow]);
expect(levels[0].price).toEqual('20');
updateLevels(levels, [removeFirstRow]);
levels = updateLevels(levels, [removeFirstRow]);
expect(levels[0].price).toEqual('20');
expect(updateLevels([], [removeFirstRow])).toEqual([]);
const addFirstRow: MarketDepthSubscription_marketDepthUpdate_sell = {
@ -107,7 +105,7 @@ describe('updateLevels', () => {
volume: '10',
numberOfOrders: '10',
};
updateLevels(levels, [addFirstRow]);
levels = updateLevels(levels, [addFirstRow]);
expect(levels[0].price).toEqual('10');
const addBeforeLastRow: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel',
@ -115,7 +113,7 @@ describe('updateLevels', () => {
volume: '95',
numberOfOrders: '95',
};
updateLevels(levels, [addBeforeLastRow]);
levels = updateLevels(levels, [addBeforeLastRow]);
expect(levels[levels.length - 2].price).toEqual('95');
const addAtTheEnd: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel',
@ -123,7 +121,7 @@ describe('updateLevels', () => {
volume: '115',
numberOfOrders: '115',
};
updateLevels(levels, [addAtTheEnd]);
levels = updateLevels(levels, [addAtTheEnd]);
expect(levels[levels.length - 1].price).toEqual('115');
const updateLastRow: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel',
@ -131,7 +129,7 @@ describe('updateLevels', () => {
volume: '116',
numberOfOrders: '115',
};
updateLevels(levels, [updateLastRow]);
levels = updateLevels(levels, [updateLastRow]);
expect(levels[levels.length - 1]).toEqual(updateLastRow);
expect(updateLevels([], [updateLastRow])).toEqual([updateLastRow]);
});

View File

@ -1,4 +1,3 @@
import produce from 'immer';
import groupBy from 'lodash/groupBy';
import { VolumeType } from '@vegaprotocol/react-helpers';
import { MarketTradingMode } from '@vegaprotocol/types';
@ -31,6 +30,8 @@ export interface OrderbookRowData {
cumulativeVol: CumulativeVol;
}
type PartialOrderbookRowData = Pick<OrderbookRowData, 'price' | 'ask' | 'bid'>;
export type OrderbookData = Partial<
Omit<MarketDepth_market_data, '__typename' | 'market'>
> & { rows: OrderbookRowData[] | null };
@ -75,21 +76,31 @@ const updateRelativeData = (data: OrderbookRowData[]) => {
});
};
export const createPartialRow = (
price: string,
volume = 0,
dataType?: VolumeType
): PartialOrderbookRowData => ({
price,
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
});
export const extendRow = (row: PartialOrderbookRowData): OrderbookRowData =>
Object.assign(row, {
cumulativeVol: {
ask: 0,
bid: 0,
},
askByLevel: row.ask ? { [row.price]: row.ask } : {},
bidByLevel: row.bid ? { [row.price]: row.bid } : {},
});
export const createRow = (
price: string,
volume = 0,
dataType?: VolumeType
): OrderbookRowData => ({
price,
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
cumulativeVol: {
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
},
askByLevel: dataType === VolumeType.ask ? { [price]: volume } : {},
bidByLevel: dataType === VolumeType.bid ? { [price]: volume } : {},
});
): OrderbookRowData => extendRow(createPartialRow(price, volume, dataType));
const mapRawData =
(dataType: VolumeType.ask | VolumeType.bid) =>
@ -99,8 +110,8 @@ const mapRawData =
| MarketDepthSubscription_marketDepthUpdate_sell
| MarketDepth_market_depth_buy
| MarketDepthSubscription_marketDepthUpdate_buy
): OrderbookRowData =>
createRow(data.price, Number(data.volume), dataType);
): PartialOrderbookRowData =>
createPartialRow(data.price, Number(data.volume), dataType);
/**
* @summary merges sell amd buy data, orders by price desc, group by price level, counts cumulative and relative values
@ -121,37 +132,38 @@ export const compactRows = (
resolution: number
) => {
// map raw sell data to OrderbookData
const askOrderbookData = [...(sell ?? [])].map<OrderbookRowData>(
const askOrderbookData = [...(sell ?? [])].map<PartialOrderbookRowData>(
mapRawData(VolumeType.ask)
);
// map raw buy data to OrderbookData
const bidOrderbookData = [...(buy ?? [])].map<OrderbookRowData>(
const bidOrderbookData = [...(buy ?? [])].map<PartialOrderbookRowData>(
mapRawData(VolumeType.bid)
);
// group by price level
const groupedByLevel = groupBy<OrderbookRowData>(
const groupedByLevel = groupBy<PartialOrderbookRowData>(
[...askOrderbookData, ...bidOrderbookData],
(row) => getPriceLevel(row.price, resolution)
);
// create single OrderbookData from grouped OrderbookData[], sum volumes and atore volume by level
const orderbookData = Object.keys(groupedByLevel).reduce<OrderbookRowData[]>(
(rows, price) =>
rows.concat(
groupedByLevel[price].reduce<OrderbookRowData>(
(a, c) => ({
...a,
ask: a.ask + c.ask,
askByLevel: Object.assign(a.askByLevel, c.askByLevel),
bid: (a.bid ?? 0) + (c.bid ?? 0),
bidByLevel: Object.assign(a.bidByLevel, c.bidByLevel),
}),
createRow(price)
)
),
[]
);
const orderbookData: OrderbookRowData[] = [];
Object.keys(groupedByLevel).forEach((price) => {
const row = extendRow(
groupedByLevel[price].pop() as PartialOrderbookRowData
);
row.price = price;
let subRow: PartialOrderbookRowData | undefined;
// eslint-disable-next-line no-cond-assign
while ((subRow = groupedByLevel[price].pop())) {
row.ask += subRow.ask;
row.bid += subRow.bid;
if (subRow.ask) {
row.askByLevel[subRow.price] = subRow.ask;
}
if (subRow.bid) {
row.bidByLevel[subRow.price] = subRow.bid;
}
}
orderbookData.push(row);
});
// order by price, it's safe to cast to number price diff should not exceed Number.MAX_SAFE_INTEGER
orderbookData.sort((a, b) => Number(BigInt(b.price) - BigInt(a.price)));
// count cumulative volumes
@ -163,11 +175,9 @@ export const compactRows = (
(i !== 0 ? orderbookData[i - 1].cumulativeVol.bid : 0);
}
for (let i = maxIndex; i >= 0; i--) {
if (!orderbookData[i].cumulativeVol.ask) {
orderbookData[i].cumulativeVol.ask =
orderbookData[i].ask +
(i !== maxIndex ? orderbookData[i + 1].cumulativeVol.ask : 0);
}
orderbookData[i].cumulativeVol.ask =
orderbookData[i].ask +
(i !== maxIndex ? orderbookData[i + 1].cumulativeVol.ask : 0);
}
}
// count relative volumes
@ -186,13 +196,13 @@ export const compactRows = (
*/
const partiallyUpdateCompactedRows = (
dataType: VolumeType,
draft: OrderbookRowData[],
data: OrderbookRowData[],
delta:
| MarketDepthSubscription_marketDepthUpdate_sell
| MarketDepthSubscription_marketDepthUpdate_buy,
resolution: number,
modifiedIndex: number
) => {
): [number, OrderbookRowData[]] => {
const { price } = delta;
const volume = Number(delta.volume);
const priceLevel = getPriceLevel(price, resolution);
@ -201,28 +211,36 @@ const partiallyUpdateCompactedRows = (
const oppositeVolKey = isAskDataType ? 'bid' : 'ask';
const volByLevelKey = isAskDataType ? 'askByLevel' : 'bidByLevel';
const resolveModifiedIndex = isAskDataType ? Math.max : Math.min;
let index = draft.findIndex((data) => data.price === priceLevel);
let index = data.findIndex((row) => row.price === priceLevel);
if (index !== -1) {
modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
draft[index][volKey] =
draft[index][volKey] - (draft[index][volByLevelKey][price] || 0) + volume;
draft[index][volByLevelKey][price] = volume;
data[index] = {
...data[index],
[volKey]:
data[index][volKey] - (data[index][volByLevelKey][price] || 0) + volume,
[volByLevelKey]: {
...data[index][volByLevelKey],
[price]: volume,
},
};
return [modifiedIndex, [...data]];
} else {
const newData: OrderbookRowData = createRow(priceLevel, volume, dataType);
index = draft.findIndex((data) => BigInt(data.price) < BigInt(priceLevel));
index = data.findIndex((row) => BigInt(row.price) < BigInt(priceLevel));
if (index !== -1) {
draft.splice(index, 0, newData);
newData.cumulativeVol[oppositeVolKey] =
draft[index + (isAskDataType ? -1 : 1)]?.cumulativeVol[
oppositeVolKey
] ?? 0;
data[index + (isAskDataType ? 0 : 1)]?.cumulativeVol[oppositeVolKey] ??
0;
modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
return [
modifiedIndex,
[...data.slice(0, index), newData, ...data.slice(index)],
];
} else {
draft.push(newData);
modifiedIndex = draft.length - 1;
modifiedIndex = data.length - 1;
return [modifiedIndex, [...data, newData]];
}
}
return modifiedIndex;
};
/**
@ -239,59 +257,66 @@ export const updateCompactedRows = (
sell: MarketDepthSubscription_marketDepthUpdate_sell[] | null,
buy: MarketDepthSubscription_marketDepthUpdate_buy[] | null,
resolution: number
) =>
produce(rows, (draft) => {
let sellModifiedIndex = -1;
sell?.forEach((delta) => {
sellModifiedIndex = partiallyUpdateCompactedRows(
VolumeType.ask,
draft,
delta,
resolution,
sellModifiedIndex
);
});
let buyModifiedIndex = draft.length;
buy?.forEach((delta) => {
buyModifiedIndex = partiallyUpdateCompactedRows(
VolumeType.bid,
draft,
delta,
resolution,
buyModifiedIndex
);
});
// update cummulative ask only below hihgest modified price level
if (sellModifiedIndex !== -1) {
for (let i = Math.min(sellModifiedIndex, draft.length - 2); i >= 0; i--) {
draft[i].cumulativeVol.ask =
draft[i + 1].cumulativeVol.ask + draft[i].ask;
}
}
// update cummulative bid only above lowest modified price level
if (buyModifiedIndex !== draft.length) {
for (
let i = Math.max(buyModifiedIndex, 1), l = draft.length;
i < l;
i++
) {
draft[i].cumulativeVol.bid =
draft[i - 1].cumulativeVol.bid + draft[i].bid;
}
}
let index = 0;
// remove levels that do not have any volume
while (index < draft.length) {
if (!draft[index].ask && !draft[index].bid) {
draft.splice(index, 1);
} else {
index += 1;
}
}
// count relative volumes
updateRelativeData(draft);
) => {
let sellModifiedIndex = -1;
let data = [...rows];
sell?.forEach((delta) => {
[sellModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.ask,
data,
delta,
resolution,
sellModifiedIndex
);
});
let buyModifiedIndex = data.length;
buy?.forEach((delta) => {
[buyModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.bid,
data,
delta,
resolution,
buyModifiedIndex
);
});
// update cummulative ask only below hihgest modified price level
if (sellModifiedIndex !== -1) {
for (let i = Math.min(sellModifiedIndex, data.length - 2); i >= 0; i--) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
ask: data[i + 1].cumulativeVol.ask + data[i].ask,
},
};
}
}
// update cummulative bid only above lowest modified price level
if (buyModifiedIndex !== data.length) {
for (let i = Math.max(buyModifiedIndex, 1), l = data.length; i < l; i++) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
bid: data[i - 1].cumulativeVol.bid + data[i].bid,
},
};
}
}
let index = 0;
// remove levels that do not have any volume
while (index < data.length) {
if (!data[index].ask && !data[index].bid) {
data.splice(index, 1);
} else {
index += 1;
}
}
// count relative volumes
updateRelativeData(data);
return data;
};
export const mapMarketData = (
data:
@ -319,23 +344,24 @@ export const mapMarketData = (
* @returns
*/
export const updateLevels = (
levels: (MarketDepth_market_depth_buy | MarketDepth_market_depth_sell)[],
draft: (MarketDepth_market_depth_buy | MarketDepth_market_depth_sell)[],
updates: (
| MarketDepthSubscription_marketDepthUpdate_buy
| MarketDepthSubscription_marketDepthUpdate_sell
)[]
) => {
const levels = [...draft];
updates.forEach((update) => {
let index = levels.findIndex((level) => level.price === update.price);
if (index !== -1) {
if (update.volume === '0') {
levels.splice(index, 1);
} else {
Object.assign(levels[index], update);
levels[index] = update;
}
} else if (update.volume !== '0') {
index = levels.findIndex(
(level) => Number(level.price) > Number(update.price)
(level) => BigInt(level.price) > BigInt(update.price)
);
if (index !== -1) {
levels.splice(index, 0, update);

View File

@ -1,5 +1,4 @@
import throttle from 'lodash/throttle';
import produce from 'immer';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Orderbook } from './orderbook';
import { useDataProvider } from '@vegaprotocol/react-helpers';
@ -25,27 +24,52 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
rows: null,
});
const dataRef = useRef<OrderbookData>({ rows: null });
const setOrderbookDataThrottled = useRef(throttle(setOrderbookData, 1000));
const deltaRef = useRef<MarketDepthSubscription_marketDepthUpdate>();
const updateOrderbookData = useRef(
throttle(() => {
if (!deltaRef.current) {
return;
}
dataRef.current = {
...deltaRef.current.market.data,
...mapMarketData(deltaRef.current.market.data, resolutionRef.current),
rows: updateCompactedRows(
dataRef.current.rows ?? [],
deltaRef.current.sell,
deltaRef.current.buy,
resolutionRef.current
),
};
deltaRef.current = undefined;
setOrderbookData(dataRef.current);
}, 1000)
);
const update = useCallback(
(delta: MarketDepthSubscription_marketDepthUpdate) => {
if (!dataRef.current.rows) {
return false;
}
dataRef.current = produce(dataRef.current, (draft) => {
Object.assign(draft, delta.market.data);
draft.rows = updateCompactedRows(
draft.rows ?? [],
delta.sell,
delta.buy,
resolutionRef.current
);
Object.assign(
draft,
mapMarketData(delta.market.data, resolutionRef.current)
);
});
setOrderbookDataThrottled.current(dataRef.current);
if (deltaRef.current) {
deltaRef.current.market = delta.market;
if (delta.sell) {
if (deltaRef.current.sell) {
deltaRef.current.sell.push(...delta.sell);
} else {
deltaRef.current.sell = delta.sell;
}
}
if (delta.buy) {
if (deltaRef.current.buy) {
deltaRef.current.buy.push(...delta.buy);
} else {
deltaRef.current.buy = delta.buy;
}
}
} else {
deltaRef.current = delta;
}
updateOrderbookData.current();
return true;
},
// using resolutionRef.current to avoid using resolution as a dependency - it will cause data provider restart on resolution change

View File

@ -176,7 +176,10 @@ export const Orderbook = ({
// adjust to current rows position
scrollTop +=
(scrollTopRef.current % rowHeight) - (scrollTop % rowHeight);
const priceCenterScrollOffset = Math.max(0, Math.min(scrollTop));
const priceCenterScrollOffset = Math.max(
0,
Math.min(scrollTop, numberOfRows * rowHeight - viewportHeight)
);
if (scrollTopRef.current !== priceCenterScrollOffset) {
updateScrollOffset(priceCenterScrollOffset);
scrollTopRef.current = priceCenterScrollOffset;
@ -184,7 +187,13 @@ export const Orderbook = ({
}
}
},
[maxPriceLevel, resolution, viewportHeight, updateScrollOffset]
[
maxPriceLevel,
resolution,
viewportHeight,
numberOfRows,
updateScrollOffset,
]
);
useEffect(() => {
@ -199,23 +208,36 @@ export const Orderbook = ({
return;
}
priceInCenter.current = undefined;
setLockOnMidPrice(true);
scrollToPrice(
getPriceLevel(
BigInt(bestStaticOfferPrice) +
(BigInt(bestStaticBidPrice) - BigInt(bestStaticOfferPrice)) /
BigInt(2),
resolution
)
let midPrice = getPriceLevel(
BigInt(bestStaticOfferPrice) +
(BigInt(bestStaticBidPrice) - BigInt(bestStaticOfferPrice)) / BigInt(2),
resolution
);
}, [bestStaticOfferPrice, bestStaticBidPrice, scrollToPrice, resolution]);
if (BigInt(midPrice) > BigInt(maxPriceLevel)) {
midPrice = maxPriceLevel;
} else {
const minPriceLevel =
BigInt(maxPriceLevel) - BigInt(Math.floor(numberOfRows * resolution));
if (BigInt(midPrice) < minPriceLevel) {
midPrice = minPriceLevel.toString();
}
}
scrollToPrice(midPrice);
setLockOnMidPrice(true);
}, [
bestStaticOfferPrice,
bestStaticBidPrice,
scrollToPrice,
resolution,
maxPriceLevel,
numberOfRows,
]);
// adjust scroll position to keep selected price in center
useLayoutEffect(() => {
if (resolutionRef.current !== resolution) {
priceInCenter.current = undefined;
resolutionRef.current = resolution;
setLockOnMidPrice(true);
}
if (priceInCenter.current) {
scrollToPrice(priceInCenter.current);
@ -238,21 +260,19 @@ export const Orderbook = ({
return () => window.removeEventListener('resize', handleResize);
}, []);
const renderedRows = useMemo(() => {
let offset = Math.max(0, Math.round(scrollOffset / rowHeight));
const prependingBufferSize = Math.min(bufferSize, offset);
offset -= prependingBufferSize;
const viewportSize = Math.round(viewportHeight / rowHeight);
const limit = Math.min(
prependingBufferSize + viewportSize + bufferSize,
numberOfRows - offset
);
return {
offset,
limit,
data: getRowsToRender(rows, resolution, offset, limit),
};
}, [rows, scrollOffset, resolution, viewportHeight, numberOfRows]);
let offset = Math.max(0, Math.round(scrollOffset / rowHeight));
const prependingBufferSize = Math.min(bufferSize, offset);
offset -= prependingBufferSize;
const viewportSize = Math.round(viewportHeight / rowHeight);
const limit = Math.min(
prependingBufferSize + viewportSize + bufferSize,
numberOfRows - offset
);
const renderedRows = {
offset,
limit,
data: getRowsToRender(rows, resolution, offset, limit),
};
const paddingTop = renderedRows.offset * rowHeight;
const paddingBottom =

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import type {
Markets,
@ -90,13 +91,14 @@ const MARKET_DATA_SUB = gql`
}
`;
const update = (draft: Markets_markets[], delta: MarketDataSub_marketData) => {
const index = draft.findIndex((m) => m.id === delta.market.id);
if (index !== -1) {
draft[index].data = delta;
}
// @TODO - else push new market to draft
};
const update = (data: Markets_markets[], delta: MarketDataSub_marketData) =>
produce(data, (draft) => {
const index = draft.findIndex((m) => m.id === delta.market.id);
if (index !== -1) {
draft[index].data = delta;
}
// @TODO - else push new market to draft
});
const getData = (responseData: Markets): Markets_markets[] | null =>
responseData.markets;
const getDelta = (subscriptionData: MarketDataSub): MarketDataSub_marketData =>

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import { makeDataProvider } from '@vegaprotocol/react-helpers';
import type { OrderFields } from './__generated__/OrderFields';
@ -78,19 +79,20 @@ export const prepareIncomingOrders = (delta: OrderFields[]) => {
return incoming;
};
const update = (draft: OrderFields[], delta: OrderFields[]) => {
const incoming = prepareIncomingOrders(delta);
const update = (data: OrderFields[], delta: OrderFields[]) =>
produce(data, (draft) => {
const incoming = prepareIncomingOrders(delta);
// Add or update incoming orders
incoming.forEach((order) => {
const index = draft.findIndex((o) => o.id === order.id);
if (index === -1) {
draft.unshift(order);
} else {
draft[index] = order;
}
// Add or update incoming orders
incoming.forEach((order) => {
const index = draft.findIndex((o) => o.id === order.id);
if (index === -1) {
draft.unshift(order);
} else {
draft[index] = order;
}
});
});
};
const getData = (responseData: Orders): Orders_party_orders[] | null =>
responseData?.party?.orders || null;

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import type {
Positions,
@ -75,16 +76,17 @@ export const POSITIONS_SUB = gql`
`;
const update = (
draft: Positions_party_positions[],
data: Positions_party_positions[],
delta: PositionSubscribe_positions
) => {
const index = draft.findIndex((m) => m.market.id === delta.market.id);
if (index !== -1) {
draft[index] = delta;
} else {
draft.push(delta);
}
};
) =>
produce(data, (draft) => {
const index = draft.findIndex((m) => m.market.id === delta.market.id);
if (index !== -1) {
draft[index] = delta;
} else {
draft.push(delta);
}
});
const getData = (responseData: Positions): Positions_party_positions[] | null =>
responseData.party ? responseData.party.positions : null;
const getDelta = (

View File

@ -2,3 +2,4 @@ export * from './use-apply-grid-transaction';
export * from './use-data-provider';
export * from './use-theme-switcher';
export * from './use-fetch';
export * from './use-resize';

View File

@ -0,0 +1,34 @@
import { useRef, useEffect, useState } from 'react';
export const useResize = () => {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
const timeout = useRef(0);
const handleResize = () => {
if (timeout.current) {
window.cancelAnimationFrame(timeout.current);
}
// Setup the new requestAnimationFrame()
timeout.current = window.requestAnimationFrame(function () {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
});
};
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
window.cancelAnimationFrame(timeout.current);
};
}, []);
return windowSize;
};

View File

@ -1,5 +1,3 @@
import { produce } from 'immer';
import type { Draft } from 'immer';
import type {
ApolloClient,
DocumentNode,
@ -34,11 +32,7 @@ export interface Subscribe<Data, Delta> {
type Query<Result> = DocumentNode | TypedDocumentNode<Result, any>;
export interface Update<Data, Delta> {
(
draft: Draft<Data>,
delta: Delta,
reload: (forceReset?: boolean) => void
): void;
(data: Data, delta: Delta, reload: (forceReset?: boolean) => void): Data;
}
interface GetData<QueryData, Data> {
@ -105,14 +99,12 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
data = getData(res.data);
// if there was some updates received from subscription during initial query loading apply them on just received data
if (data && updateQueue && updateQueue.length > 0) {
data = produce(data, (draft) => {
while (updateQueue.length) {
const delta = updateQueue.shift();
if (delta) {
update(draft, delta, reload);
}
while (updateQueue.length) {
const delta = updateQueue.shift();
if (delta) {
data = update(data, delta, reload);
}
});
}
}
} catch (e) {
// if error will occur data provider stops subscription
@ -168,9 +160,7 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
if (loading || !data) {
updateQueue.push(delta);
} else {
const newData = produce(data, (draft) => {
update(draft, delta, reload);
});
const newData = update(data, delta, reload);
if (newData === data) {
return;
}

View File

@ -11,6 +11,7 @@ module.exports = {
yellow: '#DFFF0B',
mint: '#00F780',
pink: '#FF077F',
blue: '#2E6DE5',
},
fontSize: {
...theme.fontSize,

View File

@ -4,6 +4,7 @@ import type { TradeFields } from './__generated__/TradeFields';
import type { Trades } from './__generated__/Trades';
import type { TradesSub } from './__generated__/TradesSub';
import orderBy from 'lodash/orderBy';
import produce from 'immer';
export const MAX_TRADES = 50;
@ -52,17 +53,18 @@ export const sortTrades = (trades: TradeFields[]) => {
);
};
const update = (draft: TradeFields[], delta: TradeFields[]) => {
const incoming = sortTrades(delta);
const update = (data: TradeFields[], delta: TradeFields[]) =>
produce(data, (draft) => {
const incoming = sortTrades(delta);
// Add new trades to the top
draft.unshift(...incoming);
// Add new trades to the top
draft.unshift(...incoming);
// Remove old trades from the bottom
if (draft.length > MAX_TRADES) {
draft.splice(MAX_TRADES, draft.length - MAX_TRADES);
}
};
// Remove old trades from the bottom
if (draft.length > MAX_TRADES) {
draft.splice(MAX_TRADES, draft.length - MAX_TRADES);
}
});
const getData = (responseData: Trades): TradeFields[] | null =>
responseData.market ? responseData.market.trades : null;

View File

@ -0,0 +1,25 @@
export const SunIcon = () => (
<svg viewBox="0 0 45 45" className="w-32 h-32">
<g>
<path
d="M22.5 27.79a5.29 5.29 0 1 0 0-10.58 5.29 5.29 0 0 0 0 10.58Z"
fill="currentColor"
></path>
<path
d="M15.01 22.5H10M35 22.5h-5.01M22.5 29.99V35M22.5 10v5.01M17.21 27.79l-3.55 3.55M31.34 13.66l-3.55 3.55M27.79 27.79l3.55 3.55M13.66 13.66l3.55 3.55"
stroke="currentColor"
strokeWidth="1.3"
strokeMiterlimit="10"
></path>
</g>
</svg>
);
export const MoonIcon = () => (
<svg viewBox="0 0 45 45" className="w-32 h-32">
<path
d="M28.75 11.69A12.39 12.39 0 0 0 22.5 10a12.5 12.5 0 1 0 0 25c2.196 0 4.353-.583 6.25-1.69A12.46 12.46 0 0 0 35 22.5a12.46 12.46 0 0 0-6.25-10.81Zm-6.25 22a11.21 11.21 0 0 1-11.2-11.2 11.21 11.21 0 0 1 11.2-11.2c1.246 0 2.484.209 3.66.62a13.861 13.861 0 0 0-5 10.58 13.861 13.861 0 0 0 5 10.58 11.078 11.078 0 0 1-3.66.63v-.01Z"
fill="currentColor"
></path>
</svg>
);

View File

@ -1 +1,2 @@
export * from './theme-switcher';
export * from './icons';

View File

@ -1,34 +1,31 @@
import classNames from 'classnames';
import { SunIcon, MoonIcon } from './icons';
export const ThemeSwitcher = ({
onToggle,
className,
sunClassName = '',
moonClassName = '',
}: {
onToggle: () => void;
className?: string;
}) => (
<button type="button" onClick={() => onToggle()} className={className}>
<span className="dark:hidden text-black">
<svg viewBox="0 0 45 45" className="w-32 h-32">
<g>
<path
d="M22.5 27.79a5.29 5.29 0 1 0 0-10.58 5.29 5.29 0 0 0 0 10.58Z"
fill="currentColor"
></path>
<path
d="M15.01 22.5H10M35 22.5h-5.01M22.5 29.99V35M22.5 10v5.01M17.21 27.79l-3.55 3.55M31.34 13.66l-3.55 3.55M27.79 27.79l3.55 3.55M13.66 13.66l3.55 3.55"
stroke="currentColor"
strokeWidth="1.3"
strokeMiterlimit="10"
></path>
</g>
</svg>
</span>
<span className="hidden dark:inline text-white">
<svg viewBox="0 0 45 45" className="w-32 h-32">
<path
d="M28.75 11.69A12.39 12.39 0 0 0 22.5 10a12.5 12.5 0 1 0 0 25c2.196 0 4.353-.583 6.25-1.69A12.46 12.46 0 0 0 35 22.5a12.46 12.46 0 0 0-6.25-10.81Zm-6.25 22a11.21 11.21 0 0 1-11.2-11.2 11.21 11.21 0 0 1 11.2-11.2c1.246 0 2.484.209 3.66.62a13.861 13.861 0 0 0-5 10.58 13.861 13.861 0 0 0 5 10.58 11.078 11.078 0 0 1-3.66.63v-.01Z"
fill="currentColor"
></path>
</svg>
</span>
</button>
);
sunClassName?: string;
moonClassName?: string;
}) => {
const sunClasses = classNames('dark:hidden text-black', sunClassName);
const moonClasses = classNames(
'hidden dark:inline text-white',
moonClassName
);
return (
<button type="button" onClick={() => onToggle()} className={className}>
<span className={sunClasses}>
<SunIcon />
</span>
<span className={moonClasses}>
<MoonIcon />
</span>
</button>
);
};