feat: update market data components when they are in view (#2607)
This commit is contained in:
parent
4f7b589cbd
commit
5a0da4e158
@ -215,7 +215,9 @@ export const useOrderValidation = ({
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip
|
||||
description={<DataGrid grid={compileGridData(market)} />}
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
@ -237,7 +239,9 @@ export const useOrderValidation = ({
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip
|
||||
description={<DataGrid grid={compileGridData(market)} />}
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
@ -276,7 +280,9 @@ export const useOrderValidation = ({
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip
|
||||
description={<DataGrid grid={compileGridData(market)} />}
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
@ -300,7 +306,9 @@ export const useOrderValidation = ({
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip
|
||||
description={<DataGrid grid={compileGridData(market)} />}
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
|
@ -1,27 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL subscription operation: CandleLive
|
||||
// ====================================================
|
||||
|
||||
export interface CandleLive_candles {
|
||||
__typename: "Candle";
|
||||
/**
|
||||
* Close price (uint64)
|
||||
*/
|
||||
close: string;
|
||||
}
|
||||
|
||||
export interface CandleLive {
|
||||
/**
|
||||
* Subscribe to the candles updates
|
||||
*/
|
||||
candles: CandleLive_candles;
|
||||
}
|
||||
|
||||
export interface CandleLiveVariables {
|
||||
marketId: string;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type CandleLiveSubscriptionVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CandleLiveSubscription = { __typename?: 'Subscription', candles: { __typename?: 'Candle', close: string } };
|
||||
|
||||
|
||||
export const CandleLiveDocument = gql`
|
||||
subscription CandleLive($marketId: ID!) {
|
||||
candles(marketId: $marketId, interval: INTERVAL_I1H) {
|
||||
close
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useCandleLiveSubscription__
|
||||
*
|
||||
* To run a query within a React component, call `useCandleLiveSubscription` and pass it any options that fit your needs.
|
||||
* When your component renders, `useCandleLiveSubscription` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useCandleLiveSubscription({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCandleLiveSubscription(baseOptions: Apollo.SubscriptionHookOptions<CandleLiveSubscription, CandleLiveSubscriptionVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSubscription<CandleLiveSubscription, CandleLiveSubscriptionVariables>(CandleLiveDocument, options);
|
||||
}
|
||||
export type CandleLiveSubscriptionHookResult = ReturnType<typeof useCandleLiveSubscription>;
|
||||
export type CandleLiveSubscriptionResult = Apollo.SubscriptionResult<CandleLiveSubscription>;
|
@ -15,7 +15,7 @@ import {
|
||||
import type { Candle } from '@vegaprotocol/market-list';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||
|
||||
const DEBOUNCE_UPDATE_TIME = 500;
|
||||
const THROTTLE_UPDATE_TIME = 500;
|
||||
|
||||
export const Last24hVolume = ({
|
||||
marketId,
|
||||
@ -54,7 +54,7 @@ export const Last24hVolume = ({
|
||||
const throttledSetCandles = useRef(
|
||||
throttle((data: Candle[]) => {
|
||||
setCandleVolume(calcDayVolume(data));
|
||||
}, DEBOUNCE_UPDATE_TIME)
|
||||
}, THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
|
||||
const update = useCallback(
|
||||
@ -78,7 +78,7 @@ export const Last24hVolume = ({
|
||||
throttle((candles: Candle[]) => {
|
||||
const candle24hAgo = candles?.[0];
|
||||
setVolumeChange(getChange(data || [], candle24hAgo?.close));
|
||||
}, DEBOUNCE_UPDATE_TIME)
|
||||
}, THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
|
||||
const updateCandle24hAgo = useCallback(
|
||||
|
@ -1 +0,0 @@
|
||||
window._env_ = {};
|
Binary file not shown.
Before Width: | Height: | Size: 60 KiB |
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
@ -16,7 +16,7 @@ import { MarketMarkPrice } from '../../components/market-mark-price';
|
||||
import { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||
import { Last24hVolume } from '../../components/last-24h-volume';
|
||||
import { MarketState } from '../../components/market-state';
|
||||
import { MarketTradingMode } from '../../components/market-trading-mode';
|
||||
import { HeaderStatMarketTradingMode } from '../../components/market-trading-mode';
|
||||
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
||||
import { MarketState as State } from '@vegaprotocol/types';
|
||||
|
||||
@ -64,22 +64,35 @@ export const TradeMarketHeader = ({
|
||||
>
|
||||
<ExpiryLabel market={market} />
|
||||
</HeaderStat>
|
||||
<MarketMarkPrice
|
||||
<HeaderStat heading={t('Price')} testId="market-price">
|
||||
<MarketMarkPrice
|
||||
marketId={market?.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
/>
|
||||
</HeaderStat>
|
||||
<HeaderStat heading={t('Change (24h)')} testId="market-change">
|
||||
<Last24hPriceChange
|
||||
marketId={market?.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
/>
|
||||
</HeaderStat>
|
||||
<HeaderStat
|
||||
heading={t('Volume (24h)')}
|
||||
testId="market-volume"
|
||||
description={t(
|
||||
'The total amount of assets traded in the last 24 hours.'
|
||||
)}
|
||||
>
|
||||
<Last24hVolume
|
||||
marketId={market?.id}
|
||||
positionDecimalPlaces={market?.positionDecimalPlaces}
|
||||
/>
|
||||
</HeaderStat>
|
||||
<HeaderStatMarketTradingMode
|
||||
marketId={market?.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
isHeader
|
||||
onSelect={onSelect}
|
||||
initialTradingMode={market?.tradingMode}
|
||||
/>
|
||||
<Last24hPriceChange
|
||||
marketId={market?.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
isHeader
|
||||
/>
|
||||
<Last24hVolume
|
||||
marketId={market?.id}
|
||||
positionDecimalPlaces={market?.positionDecimalPlaces}
|
||||
isHeader
|
||||
/>
|
||||
<MarketTradingMode marketId={market?.id} onSelect={onSelect} isHeader />
|
||||
<MarketState market={market} />
|
||||
{asset ? (
|
||||
<HeaderStat
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
export const DEBOUNCE_UPDATE_TIME = 500;
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const RISK_ACCEPTED_KEY = 'vega-risk-accepted';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
import type { RefObject } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import {
|
||||
isNumeric,
|
||||
t,
|
||||
useDataProvider,
|
||||
useThrottledDataProvider,
|
||||
useYesterday,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { PriceCellChange } from '@vegaprotocol/ui-toolkit';
|
||||
@ -11,8 +11,7 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
import type { Candle } from '@vegaprotocol/market-list';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
import * as constants from '../constants';
|
||||
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
@ -20,75 +19,48 @@ interface Props {
|
||||
initialValue?: string[];
|
||||
isHeader?: boolean;
|
||||
noUpdate?: boolean;
|
||||
inViewRoot?: RefObject<Element>;
|
||||
}
|
||||
|
||||
export const Last24hPriceChange = ({
|
||||
marketId,
|
||||
decimalPlaces,
|
||||
initialValue,
|
||||
isHeader = false,
|
||||
noUpdate = false,
|
||||
inViewRoot,
|
||||
}: Props) => {
|
||||
const [candlesClose, setCandlesClose] = useState<string[]>(
|
||||
initialValue || []
|
||||
);
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const yesterday = useYesterday();
|
||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||
// a rerender occurs
|
||||
const yTimestamp = useMemo(() => {
|
||||
return new Date(yesterday).toISOString();
|
||||
}, [yesterday]);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
interval: Schema.Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
since: new Date(yesterday).toISOString(),
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
[marketId, yesterday]
|
||||
);
|
||||
|
||||
const throttledSetCandles = useRef(
|
||||
throttle((data: Candle[]) => {
|
||||
if (!noUpdate) {
|
||||
const candlesClose: string[] = data
|
||||
.map((candle) => candle?.close)
|
||||
.filter((c): c is CandleClose => c !== null);
|
||||
setCandlesClose(candlesClose);
|
||||
}
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
).current;
|
||||
const update = useCallback(
|
||||
({ data }: { data: Candle[] | null }) => {
|
||||
if (data) {
|
||||
throttledSetCandles(data);
|
||||
}
|
||||
return true;
|
||||
const { data, error } = useThrottledDataProvider<Candle[], Candle>(
|
||||
{
|
||||
dataProvider: marketCandlesProvider,
|
||||
variables,
|
||||
skip: !marketId || !inView,
|
||||
},
|
||||
[throttledSetCandles]
|
||||
THROTTLE_UPDATE_TIME
|
||||
);
|
||||
|
||||
const { error } = useDataProvider<Candle[], Candle>({
|
||||
dataProvider: marketCandlesProvider,
|
||||
update,
|
||||
variables,
|
||||
skip: noUpdate || !marketId,
|
||||
});
|
||||
const candles =
|
||||
data
|
||||
?.map((candle) => candle?.close)
|
||||
.filter((c): c is CandleClose => c !== null) || initialValue;
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (error || !isNumeric(decimalPlaces)) {
|
||||
return <>-</>;
|
||||
}
|
||||
return (
|
||||
<PriceCellChange candles={candlesClose} decimalPlaces={decimalPlaces} />
|
||||
);
|
||||
}, [candlesClose, decimalPlaces, error]);
|
||||
|
||||
return isHeader ? (
|
||||
<HeaderStat heading={t('Change (24h)')} testId="market-change">
|
||||
{content}
|
||||
</HeaderStat>
|
||||
) : (
|
||||
content
|
||||
if (error || !isNumeric(decimalPlaces)) {
|
||||
return <span ref={ref}>-</span>;
|
||||
}
|
||||
return (
|
||||
<PriceCellChange
|
||||
candles={candles || []}
|
||||
decimalPlaces={decimalPlaces}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,102 +1,65 @@
|
||||
import type { RefObject } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import {
|
||||
calcCandleVolume,
|
||||
marketCandlesProvider,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
useDataProvider,
|
||||
useThrottledDataProvider,
|
||||
useYesterday,
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import * as constants from '../constants';
|
||||
import { HeaderStat } from '../header';
|
||||
import { useMemo } from 'react';
|
||||
import type { Candle } from '@vegaprotocol/market-list';
|
||||
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
positionDecimalPlaces?: number;
|
||||
noUpdate?: boolean;
|
||||
isHeader?: boolean;
|
||||
formatDecimals?: number;
|
||||
inViewRoot?: RefObject<Element>;
|
||||
initialValue?: string;
|
||||
}
|
||||
|
||||
export const Last24hVolume = ({
|
||||
marketId,
|
||||
positionDecimalPlaces,
|
||||
noUpdate = false,
|
||||
isHeader = false,
|
||||
formatDecimals,
|
||||
inViewRoot,
|
||||
initialValue,
|
||||
}: Props) => {
|
||||
const [candleVolume, setCandleVolume] = useState<string>(initialValue || '');
|
||||
const yesterday = useYesterday();
|
||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
||||
// a rerender occurs
|
||||
const yTimestamp = useMemo(() => {
|
||||
return new Date(yesterday).toISOString();
|
||||
}, [yesterday]);
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
interval: Schema.Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
since: new Date(yesterday).toISOString(),
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
[marketId, yesterday]
|
||||
);
|
||||
|
||||
const throttledSetCandles = useRef(
|
||||
throttle((data: Candle[]) => {
|
||||
noUpdate || setCandleVolume(calcCandleVolume(data) || '');
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
).current;
|
||||
const update = useCallback(
|
||||
({ data }: { data: Candle[] | null }) => {
|
||||
if (data) {
|
||||
throttledSetCandles(data);
|
||||
}
|
||||
return true;
|
||||
const { data } = useThrottledDataProvider<Candle[], Candle>(
|
||||
{
|
||||
dataProvider: marketCandlesProvider,
|
||||
variables,
|
||||
skip: !(inView && marketId),
|
||||
},
|
||||
[throttledSetCandles]
|
||||
THROTTLE_UPDATE_TIME
|
||||
);
|
||||
|
||||
const { error } = useDataProvider<Candle[], Candle>({
|
||||
dataProvider: marketCandlesProvider,
|
||||
update,
|
||||
variables,
|
||||
skip: noUpdate || !marketId,
|
||||
});
|
||||
|
||||
const formatDecimals = isHeader ? positionDecimalPlaces || 0 : 2;
|
||||
const content = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{!error && candleVolume && isNumeric(positionDecimalPlaces)
|
||||
? addDecimalsFormatNumber(
|
||||
candleVolume,
|
||||
positionDecimalPlaces,
|
||||
formatDecimals
|
||||
)
|
||||
: '-'}
|
||||
</>
|
||||
);
|
||||
}, [error, candleVolume, positionDecimalPlaces, formatDecimals]);
|
||||
return isHeader ? (
|
||||
<HeaderStat
|
||||
heading={t('Volume (24h)')}
|
||||
testId="market-volume"
|
||||
description={
|
||||
error && candleVolume && positionDecimalPlaces
|
||||
? t('The total amount of assets traded in the last 24 hours.')
|
||||
: null
|
||||
}
|
||||
>
|
||||
{content}
|
||||
</HeaderStat>
|
||||
) : (
|
||||
content
|
||||
const candleVolume = data ? calcCandleVolume(data) : initialValue;
|
||||
return (
|
||||
<span ref={ref}>
|
||||
{candleVolume && isNumeric(positionDecimalPlaces)
|
||||
? addDecimalsFormatNumber(
|
||||
candleVolume,
|
||||
positionDecimalPlaces,
|
||||
formatDecimals
|
||||
)
|
||||
: '-'}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
import type { RefObject } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
PriceCell,
|
||||
useDataProvider,
|
||||
useThrottledDataProvider,
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
@ -12,14 +12,13 @@ import type {
|
||||
MarketDataUpdateFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
import * as constants from '../constants';
|
||||
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
decimalPlaces?: number;
|
||||
isHeader?: boolean;
|
||||
noUpdate?: boolean;
|
||||
asPriceCell?: boolean;
|
||||
inViewRoot?: RefObject<Element>;
|
||||
initialValue?: string;
|
||||
}
|
||||
|
||||
@ -27,58 +26,39 @@ export const MarketMarkPrice = ({
|
||||
marketId,
|
||||
decimalPlaces,
|
||||
initialValue,
|
||||
isHeader = false,
|
||||
noUpdate = false,
|
||||
inViewRoot,
|
||||
asPriceCell,
|
||||
}: Props) => {
|
||||
const [marketPrice, setMarketPrice] = useState<string | null>(
|
||||
initialValue || null
|
||||
);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||
|
||||
const throttledSetMarketPrice = useRef(
|
||||
throttle((price: string) => {
|
||||
noUpdate || setMarketPrice(price);
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
).current;
|
||||
const update = useCallback(
|
||||
({ data: marketData }: { data: MarketData | null }) => {
|
||||
throttledSetMarketPrice(marketData?.markPrice || '');
|
||||
return true;
|
||||
const { data } = useThrottledDataProvider<
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment
|
||||
>(
|
||||
{
|
||||
dataProvider: marketDataProvider,
|
||||
variables,
|
||||
skip: !inView,
|
||||
},
|
||||
[throttledSetMarketPrice]
|
||||
THROTTLE_UPDATE_TIME
|
||||
);
|
||||
|
||||
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||
dataProvider: marketDataProvider,
|
||||
update,
|
||||
variables,
|
||||
skip: noUpdate || !marketId,
|
||||
});
|
||||
const marketPrice = data?.markPrice || initialValue;
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (!marketPrice || !isNumeric(decimalPlaces)) {
|
||||
return <>-</>;
|
||||
}
|
||||
return isHeader ? (
|
||||
<div>{addDecimalsFormatNumber(marketPrice, decimalPlaces)}</div>
|
||||
) : (
|
||||
if (!marketPrice || !isNumeric(decimalPlaces)) {
|
||||
return <span ref={ref}>-</span>;
|
||||
}
|
||||
if (asPriceCell) {
|
||||
return (
|
||||
<PriceCell
|
||||
ref={ref}
|
||||
value={Number(marketPrice)}
|
||||
valueFormatted={addDecimalsFormatNumber(marketPrice, decimalPlaces, 2)}
|
||||
/>
|
||||
);
|
||||
}, [marketPrice, decimalPlaces, isHeader]);
|
||||
|
||||
return isHeader ? (
|
||||
<HeaderStat heading={t('Price')} testId="market-price">
|
||||
{content}
|
||||
</HeaderStat>
|
||||
) : (
|
||||
content
|
||||
}
|
||||
return (
|
||||
<span ref={ref}>{addDecimalsFormatNumber(marketPrice, decimalPlaces)}</span>
|
||||
);
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ export const MarketState = ({
|
||||
const throttledSetMarketState = useRef(
|
||||
throttle((state: Schema.MarketState) => {
|
||||
setMarketState(state);
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
}, constants.THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
|
||||
const update = useCallback(
|
||||
|
@ -1,110 +1,78 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
||||
import { compileGridData, TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||
import type { RefObject } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type {
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
SingleMarketFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||
import { useStaticMarketData } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
onSelect?: (marketId: string) => void;
|
||||
isHeader?: boolean;
|
||||
noUpdate?: boolean;
|
||||
initialMode?: Schema.MarketTradingMode;
|
||||
initialTrigger?: Schema.AuctionTrigger;
|
||||
}
|
||||
|
||||
export const MarketTradingMode = ({
|
||||
marketId,
|
||||
onSelect,
|
||||
isHeader = false,
|
||||
noUpdate = false,
|
||||
initialMode,
|
||||
initialTrigger,
|
||||
}: Props) => {
|
||||
const [tradingMode, setTradingMode] =
|
||||
useState<Schema.MarketTradingMode | null>(initialMode || null);
|
||||
const [trigger, setTrigger] = useState<Schema.AuctionTrigger | null>(
|
||||
initialTrigger || null
|
||||
);
|
||||
const [market, setMarket] = useState<MarketDealTicket | null>(null);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const update = useCallback(
|
||||
({ data: marketData }: { data: MarketData | null }) => {
|
||||
if (!noUpdate && marketData) {
|
||||
setTradingMode(marketData.marketTradingMode);
|
||||
setTrigger(marketData.trigger);
|
||||
setMarket({
|
||||
...data,
|
||||
data: marketData,
|
||||
} as MarketDealTicket);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[noUpdate, data]
|
||||
);
|
||||
|
||||
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
||||
dataProvider: marketDataProvider,
|
||||
update,
|
||||
variables,
|
||||
skip: noUpdate || !marketId || !data,
|
||||
});
|
||||
|
||||
const content =
|
||||
const getTradingModeLabel = (
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger
|
||||
) => {
|
||||
return (
|
||||
(tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
trigger &&
|
||||
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
||||
? `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`
|
||||
: Schema.MarketTradingModeMapping[
|
||||
tradingMode as Schema.MarketTradingMode
|
||||
]) || '-';
|
||||
]) || '-'
|
||||
);
|
||||
};
|
||||
|
||||
return isHeader ? (
|
||||
interface HeaderStatMarketTradingModeProps {
|
||||
marketId?: string;
|
||||
onSelect?: (marketId: string) => void;
|
||||
initialTradingMode?: Schema.MarketTradingMode;
|
||||
initialTrigger?: Schema.AuctionTrigger;
|
||||
}
|
||||
|
||||
export const HeaderStatMarketTradingMode = ({
|
||||
marketId,
|
||||
onSelect,
|
||||
initialTradingMode,
|
||||
initialTrigger,
|
||||
}: HeaderStatMarketTradingModeProps) => {
|
||||
const data = useStaticMarketData(marketId);
|
||||
const tradingMode = data?.marketTradingMode ?? initialTradingMode;
|
||||
const trigger = data?.trigger ?? initialTrigger;
|
||||
|
||||
return (
|
||||
<HeaderStat
|
||||
heading={t('Trading mode')}
|
||||
description={
|
||||
market && (
|
||||
<TradingModeTooltip
|
||||
tradingMode={tradingMode}
|
||||
trigger={trigger}
|
||||
compiledGrid={compileGridData(market, onSelect)}
|
||||
/>
|
||||
)
|
||||
<TradingModeTooltip marketId={marketId} onSelect={onSelect} />
|
||||
}
|
||||
testId="market-trading-mode"
|
||||
>
|
||||
<div>{content}</div>
|
||||
<div>{getTradingModeLabel(tradingMode, trigger)}</div>
|
||||
</HeaderStat>
|
||||
) : (
|
||||
);
|
||||
};
|
||||
|
||||
export const MarketTradingMode = ({
|
||||
marketId,
|
||||
initialTradingMode,
|
||||
initialTrigger,
|
||||
inViewRoot,
|
||||
}: Omit<HeaderStatMarketTradingModeProps, 'onUpdate'> & {
|
||||
inViewRoot?: RefObject<Element>;
|
||||
}) => {
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const data = useStaticMarketData(marketId, !inView);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
tradingMode &&
|
||||
trigger && (
|
||||
<TradingModeTooltip tradingMode={tradingMode} trigger={trigger} />
|
||||
)
|
||||
}
|
||||
description={<TradingModeTooltip marketId={marketId} skip={!inView} />}
|
||||
>
|
||||
<span>{content}</span>
|
||||
<span ref={ref}>
|
||||
{getTradingModeLabel(
|
||||
data?.marketTradingMode ?? initialTradingMode,
|
||||
data?.trigger ?? initialTrigger
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export const MarketVolume = ({ marketId }: { marketId: string }) => {
|
||||
const throttledSetMarketVolume = useRef(
|
||||
throttle((volume: string) => {
|
||||
setMarketVolume(volume);
|
||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
||||
}, constants.THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
const update = useCallback(
|
||||
({ data: marketData }: { data: MarketData | null }) => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { RefObject } from 'react';
|
||||
import { FeesCell } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
calcCandleHigh,
|
||||
@ -173,7 +174,7 @@ export const columns = (
|
||||
market: Market,
|
||||
onSelect: (id: string) => void,
|
||||
onCellClick: OnCellClickHandler,
|
||||
activeMarketId?: string | null
|
||||
inViewRoot?: RefObject<HTMLElement>
|
||||
) => {
|
||||
const candlesClose = market.candles
|
||||
?.map((candle) => candle?.close)
|
||||
@ -189,7 +190,6 @@ export const columns = (
|
||||
return onSelect(id);
|
||||
}
|
||||
};
|
||||
const noUpdate = !activeMarketId || market.id !== activeMarketId;
|
||||
const selectMarketColumns: Column[] = [
|
||||
{
|
||||
kind: ColumnKind.Market,
|
||||
@ -221,8 +221,9 @@ export const columns = (
|
||||
<MarketMarkPrice
|
||||
marketId={market.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
initialValue={market.data?.markPrice.toString()}
|
||||
noUpdate={noUpdate}
|
||||
initialValue={market.data?.markPrice}
|
||||
inViewRoot={inViewRoot}
|
||||
asPriceCell
|
||||
/>
|
||||
),
|
||||
className: `${cellClassNames} max-w-[100px]`,
|
||||
@ -234,7 +235,7 @@ export const columns = (
|
||||
<Last24hPriceChange
|
||||
marketId={market.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
noUpdate={noUpdate}
|
||||
inViewRoot={inViewRoot}
|
||||
initialValue={candlesClose}
|
||||
/>
|
||||
),
|
||||
@ -317,7 +318,8 @@ export const columns = (
|
||||
marketId={market.id}
|
||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||
initialValue={candleVolume}
|
||||
noUpdate={noUpdate}
|
||||
inViewRoot={inViewRoot}
|
||||
formatDecimals={2}
|
||||
/>
|
||||
),
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
@ -329,8 +331,8 @@ export const columns = (
|
||||
value: (
|
||||
<MarketTradingMode
|
||||
marketId={market?.id}
|
||||
noUpdate={noUpdate}
|
||||
initialMode={market.tradingMode}
|
||||
inViewRoot={inViewRoot}
|
||||
initialTradingMode={market.tradingMode}
|
||||
initialTrigger={market.data?.trigger}
|
||||
/>
|
||||
),
|
||||
@ -359,9 +361,9 @@ export const columns = (
|
||||
export const columnsPositionMarkets = (
|
||||
market: Market,
|
||||
onSelect: (id: string) => void,
|
||||
inViewRoot?: RefObject<HTMLElement>,
|
||||
openVolume?: string,
|
||||
onCellClick?: OnCellClickHandler,
|
||||
activeMarketId?: string | null
|
||||
onCellClick?: OnCellClickHandler
|
||||
) => {
|
||||
const candlesClose = market.candles
|
||||
?.map((candle) => candle?.close)
|
||||
@ -377,7 +379,6 @@ export const columnsPositionMarkets = (
|
||||
}
|
||||
};
|
||||
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
||||
const noUpdate = !activeMarketId || market.id !== activeMarketId;
|
||||
const selectMarketColumns: Column[] = [
|
||||
{
|
||||
kind: ColumnKind.Market,
|
||||
@ -409,8 +410,9 @@ export const columnsPositionMarkets = (
|
||||
<MarketMarkPrice
|
||||
marketId={market.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
initialValue={market.data?.markPrice.toString()}
|
||||
noUpdate={noUpdate}
|
||||
inViewRoot={inViewRoot}
|
||||
initialValue={market.data?.markPrice}
|
||||
asPriceCell
|
||||
/>
|
||||
),
|
||||
className: cellClassNames,
|
||||
@ -422,7 +424,7 @@ export const columnsPositionMarkets = (
|
||||
<Last24hPriceChange
|
||||
marketId={market.id}
|
||||
decimalPlaces={market?.decimalPlaces}
|
||||
noUpdate={noUpdate}
|
||||
inViewRoot={inViewRoot}
|
||||
initialValue={candlesClose}
|
||||
/>
|
||||
),
|
||||
@ -503,9 +505,10 @@ export const columnsPositionMarkets = (
|
||||
value: (
|
||||
<Last24hVolume
|
||||
marketId={market.id}
|
||||
inViewRoot={inViewRoot}
|
||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||
initialValue={candleVolume}
|
||||
noUpdate={noUpdate}
|
||||
formatDecimals={2}
|
||||
/>
|
||||
),
|
||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||
@ -517,8 +520,8 @@ export const columnsPositionMarkets = (
|
||||
value: (
|
||||
<MarketTradingMode
|
||||
marketId={market?.id}
|
||||
noUpdate={noUpdate}
|
||||
initialMode={market.tradingMode}
|
||||
inViewRoot={inViewRoot}
|
||||
initialTradingMode={market.tradingMode}
|
||||
initialTrigger={market.data?.trigger}
|
||||
/>
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import type { RefObject } from 'react';
|
||||
import { useMarketList } from '@vegaprotocol/market-list';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
@ -27,7 +28,6 @@ import {
|
||||
TOKEN_NEW_MARKET_PROPOSAL,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export type Market = MarketWithCandles & MarketWithData;
|
||||
|
||||
@ -36,19 +36,22 @@ export const SelectAllMarketsTableBody = ({
|
||||
positions,
|
||||
onSelect,
|
||||
onCellClick,
|
||||
activeMarketId,
|
||||
inViewRoot,
|
||||
headers = columnHeaders,
|
||||
tableColumns = (market) =>
|
||||
columns(market, onSelect, onCellClick, activeMarketId),
|
||||
tableColumns = (market) => columns(market, onSelect, onCellClick, inViewRoot),
|
||||
}: {
|
||||
markets?: Market[] | null;
|
||||
positions?: PositionFieldsFragment[];
|
||||
title?: string;
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
activeMarketId?: string | null;
|
||||
headers?: Column[];
|
||||
tableColumns?: (market: Market, openVolume?: string) => Column[];
|
||||
tableColumns?: (
|
||||
market: Market,
|
||||
inViewRoot?: RefObject<HTMLDivElement>,
|
||||
openVolume?: string
|
||||
) => Column[];
|
||||
inViewRoot?: RefObject<HTMLDivElement>;
|
||||
}) => {
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
if (!markets) return null;
|
||||
@ -68,6 +71,7 @@ export const SelectAllMarketsTableBody = ({
|
||||
onSelect={onSelect}
|
||||
columns={tableColumns(
|
||||
market,
|
||||
inViewRoot,
|
||||
positions &&
|
||||
positions.find((p) => p.market.id === market.id)?.openVolume
|
||||
)}
|
||||
@ -95,11 +99,11 @@ export const SelectMarketPopover = ({
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
}) => {
|
||||
const activeMarketId = useGlobalStore((store) => store.marketId);
|
||||
const triggerClasses =
|
||||
'sm:text-lg md:text-xl lg:text-2xl flex items-center gap-2 whitespace-nowrap hover:text-neutral-500 dark:hover:text-neutral-300 mt-1';
|
||||
const { pubKey } = useVegaWallet();
|
||||
const [open, setOpen] = useState(false);
|
||||
const inViewRoot = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
data,
|
||||
loading: marketsLoading,
|
||||
@ -155,6 +159,7 @@ export const SelectMarketPopover = ({
|
||||
<div
|
||||
className="w-[90vw] max-h-[80vh] overflow-y-auto"
|
||||
data-testid="select-market-list"
|
||||
ref={inViewRoot}
|
||||
>
|
||||
{marketsLoading || (pubKey && positionsLoading) ? (
|
||||
<div className="flex items-center gap-4">
|
||||
@ -167,6 +172,7 @@ export const SelectMarketPopover = ({
|
||||
<>
|
||||
<TableTitle>{t('My markets')}</TableTitle>
|
||||
<SelectAllMarketsTableBody
|
||||
inViewRoot={inViewRoot}
|
||||
markets={markets}
|
||||
positions={party?.positionsConnection?.edges
|
||||
?.filter((edge) => edge.node)
|
||||
@ -174,13 +180,13 @@ export const SelectMarketPopover = ({
|
||||
onSelect={onSelectMarket}
|
||||
onCellClick={onCellClick}
|
||||
headers={columnHeadersPositionMarkets}
|
||||
tableColumns={(market, openVolume) =>
|
||||
tableColumns={(market, inViewRoot, openVolume) =>
|
||||
columnsPositionMarkets(
|
||||
market,
|
||||
onSelectMarket,
|
||||
inViewRoot,
|
||||
openVolume,
|
||||
onCellClick,
|
||||
activeMarketId
|
||||
onCellClick
|
||||
)
|
||||
}
|
||||
/>
|
||||
@ -188,10 +194,10 @@ export const SelectMarketPopover = ({
|
||||
) : null}
|
||||
<TableTitle>{t('All markets')}</TableTitle>
|
||||
<SelectAllMarketsTableBody
|
||||
inViewRoot={inViewRoot}
|
||||
markets={data}
|
||||
onSelect={onSelectMarket}
|
||||
onCellClick={onCellClick}
|
||||
activeMarketId={activeMarketId}
|
||||
/>
|
||||
</table>
|
||||
)}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import 'jest-canvas-mock';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
import { defaultFallbackInView } from 'react-intersection-observer';
|
||||
|
||||
defaultFallbackInView(true);
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
// Required by radix-ui/react-tooltip
|
||||
|
@ -78,7 +78,11 @@ export const TimeInForceSelector = ({
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
|
||||
<Tooltip
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
@ -93,7 +97,11 @@ export const TimeInForceSelector = ({
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
|
||||
<Tooltip
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
|
@ -33,7 +33,11 @@ export const TypeSelector = ({
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
|
||||
<Tooltip
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
@ -46,7 +50,11 @@ export const TypeSelector = ({
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip description={<DataGrid grid={compileGridData(market)} />}>
|
||||
<Tooltip
|
||||
description={
|
||||
<DataGrid grid={compileGridData(market, market.data)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
|
@ -8,17 +8,31 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
||||
import type { Market, MarketData } from '@vegaprotocol/market-list';
|
||||
|
||||
export const compileGridData = (
|
||||
market: MarketDealTicket,
|
||||
market: Pick<
|
||||
Market,
|
||||
'tradableInstrument' | 'id' | 'decimalPlaces' | 'positionDecimalPlaces'
|
||||
>,
|
||||
marketData: Pick<
|
||||
MarketData,
|
||||
| 'marketTradingMode'
|
||||
| 'auctionStart'
|
||||
| 'auctionEnd'
|
||||
| 'indicativePrice'
|
||||
| 'indicativeVolume'
|
||||
| 'suppliedStake'
|
||||
| 'targetStake'
|
||||
| 'trigger'
|
||||
>,
|
||||
onSelect?: (id: string) => void
|
||||
): { label: ReactNode; value?: ReactNode }[] => {
|
||||
const grid: DataGridProps['grid'] = [];
|
||||
const isLiquidityMonitoringAuction =
|
||||
market.data.marketTradingMode ===
|
||||
marketData.marketTradingMode ===
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
market.data.trigger === Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY;
|
||||
marketData.trigger === Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY;
|
||||
|
||||
const formatStake = (value: string) => {
|
||||
const formattedValue = addDecimalsFormatNumber(
|
||||
@ -30,19 +44,17 @@ export const compileGridData = (
|
||||
return `${formattedValue} ${asset}`;
|
||||
};
|
||||
|
||||
if (!market.data) return grid;
|
||||
if (!marketData) return grid;
|
||||
|
||||
if (market.data.auctionStart) {
|
||||
if (marketData.auctionStart) {
|
||||
grid.push({
|
||||
label: t('Auction start'),
|
||||
value: getDateTimeFormat().format(new Date(market.data.auctionStart)),
|
||||
value: getDateTimeFormat().format(new Date(marketData.auctionStart)),
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data.auctionEnd) {
|
||||
const endDate = getDateTimeFormat().format(
|
||||
new Date(market.data.auctionEnd)
|
||||
);
|
||||
if (marketData.auctionEnd) {
|
||||
const endDate = getDateTimeFormat().format(new Date(marketData.auctionEnd));
|
||||
grid.push({
|
||||
label: isLiquidityMonitoringAuction
|
||||
? t('Est. auction end')
|
||||
@ -51,14 +63,14 @@ export const compileGridData = (
|
||||
});
|
||||
}
|
||||
|
||||
if (isLiquidityMonitoringAuction && market.data.targetStake) {
|
||||
if (isLiquidityMonitoringAuction && marketData.targetStake) {
|
||||
grid.push({
|
||||
label: t('Target liquidity'),
|
||||
value: formatStake(market.data.targetStake),
|
||||
value: formatStake(marketData.targetStake),
|
||||
});
|
||||
}
|
||||
|
||||
if (isLiquidityMonitoringAuction && market.data.suppliedStake) {
|
||||
if (isLiquidityMonitoringAuction && marketData.suppliedStake) {
|
||||
grid.push({
|
||||
label: (
|
||||
<Link
|
||||
@ -68,31 +80,31 @@ export const compileGridData = (
|
||||
<UILink>{t('Current liquidity')}</UILink>
|
||||
</Link>
|
||||
),
|
||||
value: formatStake(market.data.suppliedStake),
|
||||
value: formatStake(marketData.suppliedStake),
|
||||
});
|
||||
}
|
||||
if (market.data.indicativePrice) {
|
||||
if (marketData.indicativePrice) {
|
||||
grid.push({
|
||||
label: t('Est. uncrossing price'),
|
||||
value:
|
||||
market.data.indicativePrice && market.data.indicativePrice !== '0'
|
||||
marketData.indicativePrice && marketData.indicativePrice !== '0'
|
||||
? `~
|
||||
${addDecimalsFormatNumber(
|
||||
market.data.indicativePrice,
|
||||
marketData.indicativePrice,
|
||||
market.decimalPlaces
|
||||
)}`
|
||||
: '-',
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data.indicativeVolume) {
|
||||
if (marketData.indicativeVolume) {
|
||||
grid.push({
|
||||
label: t('Est. uncrossing vol'),
|
||||
value:
|
||||
market.data.indicativeVolume && market.data.indicativeVolume !== '0'
|
||||
marketData.indicativeVolume && marketData.indicativeVolume !== '0'
|
||||
? '~' +
|
||||
addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
marketData.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
)
|
||||
: '-',
|
||||
|
@ -1,23 +1,35 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { DataGrid, t } from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { createDocsLinks } from '@vegaprotocol/react-helpers';
|
||||
import { compileGridData } from './compile-grid-data';
|
||||
import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
|
||||
|
||||
type TradingModeTooltipProps = {
|
||||
tradingMode: Schema.MarketTradingMode | null;
|
||||
trigger: Schema.AuctionTrigger | null;
|
||||
compiledGrid?: { label: ReactNode; value?: ReactNode }[];
|
||||
marketId?: string;
|
||||
onSelect?: (marketId: string) => void;
|
||||
skip?: boolean;
|
||||
};
|
||||
|
||||
export const TradingModeTooltip = ({
|
||||
tradingMode,
|
||||
trigger,
|
||||
compiledGrid,
|
||||
marketId,
|
||||
onSelect,
|
||||
skip,
|
||||
}: TradingModeTooltipProps) => {
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const market = useMarket(marketId);
|
||||
const marketData = useStaticMarketData(marketId, skip);
|
||||
|
||||
if (!market || !marketData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const compiledGrid =
|
||||
onSelect && compileGridData(market, marketData, onSelect);
|
||||
const { marketTradingMode: tradingMode, trigger } = marketData;
|
||||
|
||||
switch (tradingMode) {
|
||||
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||
return (
|
||||
|
@ -1,4 +1,9 @@
|
||||
import produce from 'immer';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
makeDerivedDataProvider,
|
||||
useDataProvider,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
MarketDataDocument,
|
||||
@ -39,3 +44,51 @@ export const marketDataProvider = makeDataProvider<
|
||||
getData,
|
||||
getDelta,
|
||||
});
|
||||
|
||||
export type StaticMarketData = Pick<
|
||||
MarketData,
|
||||
| 'marketTradingMode'
|
||||
| 'auctionStart'
|
||||
| 'auctionEnd'
|
||||
| 'indicativePrice'
|
||||
| 'indicativeVolume'
|
||||
| 'suppliedStake'
|
||||
| 'targetStake'
|
||||
| 'trigger'
|
||||
>;
|
||||
|
||||
export const staticMarketDataProvider = makeDerivedDataProvider<
|
||||
StaticMarketData,
|
||||
never
|
||||
>([marketDataProvider], (parts, variables, prevData) => {
|
||||
const marketData = parts[0] as ReturnType<typeof getData>;
|
||||
if (!marketData) {
|
||||
return marketData;
|
||||
}
|
||||
const data: StaticMarketData = {
|
||||
marketTradingMode: marketData.marketTradingMode,
|
||||
auctionStart: marketData.auctionStart,
|
||||
auctionEnd: marketData.auctionEnd,
|
||||
indicativePrice: marketData.indicativePrice,
|
||||
indicativeVolume: marketData.indicativeVolume,
|
||||
suppliedStake: marketData.suppliedStake,
|
||||
targetStake: marketData.targetStake,
|
||||
trigger: marketData.trigger,
|
||||
};
|
||||
if (!prevData) {
|
||||
return data;
|
||||
}
|
||||
return produce(prevData, (draft) => {
|
||||
Object.assign(draft, data);
|
||||
});
|
||||
});
|
||||
|
||||
export const useStaticMarketData = (marketId?: string, skip?: boolean) => {
|
||||
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: staticMarketDataProvider,
|
||||
variables,
|
||||
skip: skip || !marketId,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
@ -35,6 +35,28 @@ export const marketsProvider = makeDataProvider<
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
const marketProvider = makeDerivedDataProvider<
|
||||
Market,
|
||||
never,
|
||||
{ marketId: string }
|
||||
>(
|
||||
[marketsProvider],
|
||||
([markets], variables) =>
|
||||
((markets as ReturnType<typeof getData>) || []).find(
|
||||
(market) => market.id === variables?.marketId
|
||||
) || null
|
||||
);
|
||||
|
||||
export const useMarket = (marketId?: string) => {
|
||||
const variables = useMemo(() => ({ marketId: marketId || '' }), [marketId]);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const activeMarketsProvider = makeDerivedDataProvider<Market[], never>(
|
||||
[marketsProvider],
|
||||
([markets]) => filterAndSortMarkets(markets)
|
||||
|
@ -86,6 +86,8 @@ describe('isNumeric', () => {
|
||||
{ i: '--123.01', o: false },
|
||||
{ i: '123.', o: false },
|
||||
{ i: '123.1.1', o: false },
|
||||
{ i: BigInt(123), o: true },
|
||||
{ i: BigInt(-1), o: true },
|
||||
{ i: new BigNumber(123), o: true },
|
||||
{ i: new BigNumber(123.123), o: true },
|
||||
{ i: new BigNumber(123.123).toString(), o: true },
|
||||
@ -98,7 +100,7 @@ describe('isNumeric', () => {
|
||||
i,
|
||||
o,
|
||||
}: {
|
||||
i: number | string | undefined | null | BigNumber;
|
||||
i: number | string | undefined | null | BigNumber | bigint;
|
||||
o: boolean;
|
||||
}) => {
|
||||
expect(isNumeric(i)).toStrictEqual(o);
|
||||
|
@ -138,7 +138,7 @@ export const useNumberParts = (
|
||||
};
|
||||
|
||||
export const isNumeric = (
|
||||
value?: string | number | BigNumber | null
|
||||
value?: string | number | BigNumber | bigint | null
|
||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||
|
||||
const INFINITY = '∞';
|
||||
|
@ -560,7 +560,8 @@ export type CombineDerivedData<
|
||||
Variables extends OperationVariables = OperationVariables
|
||||
> = (
|
||||
data: DerivedPart<Variables>['data'][],
|
||||
variables?: Variables
|
||||
variables: Variables | undefined,
|
||||
prevData: Data | null
|
||||
) => Data | null;
|
||||
|
||||
export type CombineDerivedDelta<
|
||||
@ -641,7 +642,8 @@ function makeDerivedDataProviderInternal<
|
||||
const newData = newLoaded
|
||||
? combineData(
|
||||
parts.map((part) => part.data),
|
||||
variables
|
||||
variables,
|
||||
data
|
||||
)
|
||||
: data;
|
||||
if (
|
||||
@ -655,7 +657,7 @@ function makeDerivedDataProviderInternal<
|
||||
loaded = newLoaded;
|
||||
const previousData = data;
|
||||
data = newData;
|
||||
if (newLoaded) {
|
||||
if (loaded) {
|
||||
const updatedPart = parts[updatedPartIndex];
|
||||
if (updatedPart.isUpdate) {
|
||||
isUpdate = true;
|
||||
|
@ -1,37 +1,41 @@
|
||||
import React from 'react';
|
||||
import { getDecimalSeparator } from '../format';
|
||||
import { memo, forwardRef } from 'react';
|
||||
import { getDecimalSeparator, isNumeric } from '../format';
|
||||
export interface IPriceCellProps {
|
||||
value: number | bigint | null | undefined;
|
||||
valueFormatted: string;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export const PriceCell = React.memo(
|
||||
({ value, valueFormatted, testId }: IPriceCellProps) => {
|
||||
if (
|
||||
(!value && value !== 0) ||
|
||||
(typeof value === 'number' && isNaN(Number(value)))
|
||||
) {
|
||||
return <span data-testid="price">-</span>;
|
||||
export const PriceCell = memo(
|
||||
forwardRef<HTMLSpanElement, IPriceCellProps>(
|
||||
({ value, valueFormatted, testId }: IPriceCellProps, ref) => {
|
||||
if (!isNumeric(value)) {
|
||||
return (
|
||||
<span data-testid="price" ref={ref}>
|
||||
-
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const decimalSeparator = getDecimalSeparator();
|
||||
const valueSplit: string[] = decimalSeparator
|
||||
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
||||
: [`${value}`];
|
||||
return (
|
||||
<span
|
||||
ref={ref}
|
||||
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
||||
data-testid={testId || 'price'}
|
||||
title={valueFormatted}
|
||||
>
|
||||
{valueSplit[0]}
|
||||
{valueSplit[1] ? decimalSeparator : null}
|
||||
{valueSplit[1] ? (
|
||||
<span className="opacity-60">{valueSplit[1]}</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const decimalSeparator = getDecimalSeparator();
|
||||
const valueSplit: string[] = decimalSeparator
|
||||
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
||||
: [`${value}`];
|
||||
return (
|
||||
<span
|
||||
className="font-mono relative text-black dark:text-white whitespace-nowrap overflow-hidden text-ellipsis text-right rtl-dir"
|
||||
data-testid={testId || 'price'}
|
||||
title={valueFormatted}
|
||||
>
|
||||
{valueSplit[0]}
|
||||
{valueSplit[1] ? decimalSeparator : null}
|
||||
{valueSplit[1] ? (
|
||||
<span className="opacity-60">{valueSplit[1]}</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
PriceCell.displayName = 'PriceCell';
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
formatNumberPercentage,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
import { memo, forwardRef } from 'react';
|
||||
import { signedNumberCssClass } from '@vegaprotocol/react-helpers';
|
||||
import { Arrow } from '../arrows/arrow';
|
||||
|
||||
@ -36,30 +36,33 @@ export const priceChange = (candles: string[]) => {
|
||||
: 0;
|
||||
};
|
||||
|
||||
export const PriceCellChange = React.memo(
|
||||
({ candles, decimalPlaces }: PriceChangeCellProps) => {
|
||||
const change = priceChange(candles);
|
||||
const changePercentage = priceChangePercentage(candles);
|
||||
return (
|
||||
<span
|
||||
className={`${signedNumberCssClass(
|
||||
change
|
||||
)} flex items-center gap-2 justify-end font-mono text-ui-small`}
|
||||
>
|
||||
<Arrow value={change} />
|
||||
<span data-testid="price-change-percentage">
|
||||
{formatNumberPercentage(
|
||||
new BigNumber(changePercentage.toString()),
|
||||
2
|
||||
)}
|
||||
|
||||
export const PriceCellChange = memo(
|
||||
forwardRef<HTMLSpanElement, PriceChangeCellProps>(
|
||||
({ candles, decimalPlaces }: PriceChangeCellProps, ref) => {
|
||||
const change = priceChange(candles);
|
||||
const changePercentage = priceChangePercentage(candles);
|
||||
return (
|
||||
<span
|
||||
ref={ref}
|
||||
className={`${signedNumberCssClass(
|
||||
change
|
||||
)} flex items-center gap-2 justify-end font-mono text-ui-small`}
|
||||
>
|
||||
<Arrow value={change} />
|
||||
<span data-testid="price-change-percentage">
|
||||
{formatNumberPercentage(
|
||||
new BigNumber(changePercentage.toString()),
|
||||
2
|
||||
)}
|
||||
|
||||
</span>
|
||||
<span data-testid="price-change">
|
||||
{addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
|
||||
</span>
|
||||
</span>
|
||||
<span data-testid="price-change">
|
||||
{addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
PriceCellChange.displayName = 'PriceCellChange';
|
||||
|
Loading…
Reference in New Issue
Block a user