feat(trading): add liquidity provision link to orders table (#3432)

This commit is contained in:
m.ray 2023-04-18 08:55:11 -04:00 committed by GitHub
parent 2346b2f1a0
commit 045dace274
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 125 additions and 19 deletions

View File

@ -29,7 +29,10 @@ import { LiquidityContainer } from '../liquidity/liquidity';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import type { PinnedAsset } from '@vegaprotocol/accounts'; import type { PinnedAsset } from '@vegaprotocol/accounts';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
@ -79,6 +82,7 @@ const MarketBottomPanel = memo(
({ marketId, pinnedAsset }: BottomPanelProps) => { ({ marketId, pinnedAsset }: BottomPanelProps) => {
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
return 'xxxl' === screenSize ? ( return 'xxxl' === screenSize ? (
<ResizableGrid proportionalLayout minSize={200}> <ResizableGrid proportionalLayout minSize={200}>
@ -94,6 +98,7 @@ const MarketBottomPanel = memo(
<TradingViews.Orders <TradingViews.Orders
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder enforceBottomPlaceholder
/> />
</VegaWalletContainer> </VegaWalletContainer>
@ -150,6 +155,7 @@ const MarketBottomPanel = memo(
<TradingViews.Orders <TradingViews.Orders
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder enforceBottomPlaceholder
/> />
</VegaWalletContainer> </VegaWalletContainer>
@ -293,6 +299,7 @@ interface TradePanelsProps {
market: Market | null; market: Market | null;
onSelect: (marketId: string, metaKey?: boolean) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
onOrderTypeClick?: (marketId: string) => void;
onClickCollateral: () => void; onClickCollateral: () => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
} }
@ -303,12 +310,16 @@ export const TradePanels = ({
onClickCollateral, onClickCollateral,
pinnedAsset, pinnedAsset,
}: TradePanelsProps) => { }: TradePanelsProps) => {
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
const [view, setView] = useState<TradingView>('Candles'); const [view, setView] = useState<TradingView>('Candles');
const renderView = () => { const renderView = () => {
const Component = memo<{ const Component = memo<{
marketId: string; marketId: string;
onSelect: (marketId: string, metaKey?: boolean) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
onOrderTypeClick?: (marketId: string) => void;
onClickCollateral: () => void; onClickCollateral: () => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
}>(TradingViews[view]); }>(TradingViews[view]);
@ -325,6 +336,8 @@ export const TradePanels = ({
onSelect={onSelect} onSelect={onSelect}
onClickCollateral={onClickCollateral} onClickCollateral={onClickCollateral}
pinnedAsset={pinnedAsset} pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
/> />
); );
}; };

View File

@ -19,7 +19,10 @@ import { usePageTitleStore } from '../../stores';
import { LedgerContainer } from '@vegaprotocol/ledger'; import { LedgerContainer } from '@vegaprotocol/ledger';
import { AccountsContainer } from '../../components/accounts-container'; import { AccountsContainer } from '../../components/accounts-container';
import { AccountHistoryContainer } from './account-history-container'; import { AccountHistoryContainer } from './account-history-container';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
export const Portfolio = () => { export const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
@ -31,6 +34,7 @@ export const Portfolio = () => {
}, [updateTitle]); }, [updateTitle]);
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
const wrapperClasses = 'h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
return ( return (
@ -54,7 +58,10 @@ export const Portfolio = () => {
</Tab> </Tab>
<Tab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<VegaWalletContainer> <VegaWalletContainer>
<OrderListContainer onMarketClick={onMarketClick} /> <OrderListContainer
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>

View File

@ -19,3 +19,21 @@ export const useMarketClickHandler = (replace = false) => {
[navigate, marketId, replace, isMarketPage] [navigate, marketId, replace, isMarketPage]
); );
}; };
export const useMarketLiquidityClickHandler = (replace = false) => {
const navigate = useNavigate();
const { marketId } = useParams();
const { pathname } = useLocation();
const isLiquidityPage = pathname.match(/^\/liquidity\/(.+)/);
return useCallback(
(selectedId: string, metaKey?: boolean) => {
const link = Links[Routes.LIQUIDITY](selectedId);
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else if (selectedId !== marketId || !isLiquidityPage) {
navigate(link, { replace });
}
},
[navigate, marketId, replace, isLiquidityPage]
);
};

View File

@ -31,7 +31,7 @@ export enum Routes {
MARKET = '/markets', MARKET = '/markets',
MARKETS = '/markets/all', MARKETS = '/markets/all',
PORTFOLIO = '/portfolio', PORTFOLIO = '/portfolio',
LIQUIDITY = 'liquidity/:marketId', LIQUIDITY = '/liquidity',
} }
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string }; type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
@ -41,8 +41,10 @@ export const Links: ConsoleLinks = {
marketId ? trimEnd(`${Routes.MARKET}/${marketId}`, '/') : Routes.MARKET, marketId ? trimEnd(`${Routes.MARKET}/${marketId}`, '/') : Routes.MARKET,
[Routes.MARKETS]: () => Routes.MARKETS, [Routes.MARKETS]: () => Routes.MARKETS,
[Routes.PORTFOLIO]: () => Routes.PORTFOLIO, [Routes.PORTFOLIO]: () => Routes.PORTFOLIO,
[Routes.LIQUIDITY]: (marketId: string) => [Routes.LIQUIDITY]: (marketId: string | null | undefined) =>
Routes.LIQUIDITY.replace(':marketId', marketId), marketId
? trimEnd(`${Routes.LIQUIDITY}/${marketId}`, '/')
: Routes.LIQUIDITY,
}; };
const routerConfig: RouteObject[] = [ const routerConfig: RouteObject[] = [
@ -70,6 +72,16 @@ const routerConfig: RouteObject[] = [
{ {
path: Routes.LIQUIDITY, path: Routes.LIQUIDITY,
element: <LazyLiquidity />, element: <LazyLiquidity />,
children: [
{
index: true,
element: <LazyLiquidity />,
},
{
path: ':marketId',
element: <LazyLiquidity />,
},
],
}, },
{ {
path: Routes.PORTFOLIO, path: Routes.PORTFOLIO,

View File

@ -10,6 +10,7 @@ export * from './lib/cells/price-flash-cell';
export * from './lib/cells/vol-cell'; export * from './lib/cells/vol-cell';
export * from './lib/cells/centered-grid-cell'; export * from './lib/cells/centered-grid-cell';
export * from './lib/cells/market-name-cell'; export * from './lib/cells/market-name-cell';
export * from './lib/cells/order-type-cell';
export * from './lib/filters/date-range-filter'; export * from './lib/filters/date-range-filter';
export * from './lib/filters/set-filter'; export * from './lib/filters/set-filter';

View File

@ -0,0 +1,52 @@
import type { MouseEvent } from 'react';
import { useMemo } from 'react';
import { useCallback } from 'react';
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
interface OrderTypeCellProps {
value?: Schema.OrderType;
data?: Schema.Order;
onClick?: (marketId: string, metaKey?: boolean) => void;
}
export const OrderTypeCell = ({
value,
data: order,
onClick,
}: OrderTypeCellProps) => {
const id = order ? order.market.id : '';
const label = useMemo(() => {
if (!order) {
return undefined;
}
if (!value) return '-';
if (order?.peggedOrder) {
return t('Pegged');
}
if (order?.liquidityProvision) {
return t('Liquidity provision');
}
return Schema.OrderTypeMapping[value];
}, [order, value]);
const handleOnClick = useCallback(
(ev: MouseEvent<HTMLButtonElement>) => {
ev.preventDefault();
ev.stopPropagation();
if (onClick) {
onClick(id, ev.metaKey || ev.ctrlKey);
}
},
[id, onClick]
);
if (!order) return null;
return order?.liquidityProvision ? (
<button onClick={handleOnClick} tabIndex={0} className="underline">
{label}
</button>
) : (
<span>{label}</span>
);
};

View File

@ -6,10 +6,12 @@ import { OrderListManager } from './order-list-manager';
export const OrderListContainer = ({ export const OrderListContainer = ({
marketId, marketId,
onMarketClick, onMarketClick,
onOrderTypeClick,
enforceBottomPlaceholder, enforceBottomPlaceholder,
}: { }: {
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
enforceBottomPlaceholder?: boolean; enforceBottomPlaceholder?: boolean;
}) => { }) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
@ -23,6 +25,7 @@ export const OrderListContainer = ({
partyId={pubKey} partyId={pubKey}
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
enforceBottomPlaceholder={enforceBottomPlaceholder} enforceBottomPlaceholder={enforceBottomPlaceholder}
/> />

View File

@ -25,6 +25,7 @@ export interface OrderListManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
enforceBottomPlaceholder?: boolean; enforceBottomPlaceholder?: boolean;
} }
@ -52,6 +53,7 @@ export const OrderListManager = ({
partyId, partyId,
marketId, marketId,
onMarketClick, onMarketClick,
onOrderTypeClick,
isReadOnly, isReadOnly,
enforceBottomPlaceholder, enforceBottomPlaceholder,
}: OrderListManagerProps) => { }: OrderListManagerProps) => {
@ -165,6 +167,7 @@ export const OrderListManager = ({
cancel={cancel} cancel={cancel}
setEditOrder={setEditOrder} setEditOrder={setEditOrder}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
blockLoadDebounceMillis={100} blockLoadDebounceMillis={100}
suppressLoadingOverlay suppressLoadingOverlay

View File

@ -16,6 +16,7 @@ import {
negativeClassNames, negativeClassNames,
positiveClassNames, positiveClassNames,
MarketNameCell, MarketNameCell,
OrderTypeCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { import type {
TypedDataAgGrid, TypedDataAgGrid,
@ -31,12 +32,16 @@ export type OrderListTableProps = OrderListProps & {
cancel: (order: Order) => void; cancel: (order: Order) => void;
setEditOrder: (order: Order) => void; setEditOrder: (order: Order) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
}; };
export const OrderListTable = memo( export const OrderListTable = memo(
forwardRef<AgGridReact, OrderListTableProps>( forwardRef<AgGridReact, OrderListTableProps>(
({ cancel, setEditOrder, onMarketClick, ...props }, ref) => { (
{ cancel, setEditOrder, onMarketClick, onOrderTypeClick, ...props },
ref
) => {
return ( return (
<AgGrid <AgGrid
ref={ref} ref={ref}
@ -51,7 +56,7 @@ export const OrderListTable = memo(
height: '100%', height: '100%',
}} }}
getRowId={({ data }) => data.id} getRowId={({ data }) => data.id}
components={{ MarketNameCell }} components={{ MarketNameCell, OrderTypeCell }}
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
@ -103,17 +108,9 @@ export const OrderListTable = memo(
filterParams={{ filterParams={{
set: Schema.OrderTypeMapping, set: Schema.OrderTypeMapping,
}} }}
valueFormatter={({ cellRenderer="OrderTypeCell"
data: order, cellRendererParams={{
value, onClick: onOrderTypeClick,
}: VegaValueFormatterParams<Order, 'type'>) => {
if (!order) {
return undefined;
}
if (!value) return '-';
if (order?.peggedOrder) return t('Pegged');
if (order?.liquidityProvision) return t('Liquidity provision');
return Schema.OrderTypeMapping[value];
}} }}
minWidth={80} minWidth={80}
/> />