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>
|
<span>
|
||||||
{t('This market is in auction until it reaches')}{' '}
|
{t('This market is in auction until it reaches')}{' '}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={<DataGrid grid={compileGridData(market)} />}
|
description={
|
||||||
|
<DataGrid grid={compileGridData(market, market.data)} />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span>{t('sufficient liquidity')}</span>
|
<span>{t('sufficient liquidity')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -237,7 +239,9 @@ export const useOrderValidation = ({
|
|||||||
<span>
|
<span>
|
||||||
{t('This market is in auction due to')}{' '}
|
{t('This market is in auction due to')}{' '}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={<DataGrid grid={compileGridData(market)} />}
|
description={
|
||||||
|
<DataGrid grid={compileGridData(market, market.data)} />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span>{t('high price volatility')}</span>
|
<span>{t('high price volatility')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -276,7 +280,9 @@ export const useOrderValidation = ({
|
|||||||
<span>
|
<span>
|
||||||
{t('This market is in auction until it reaches')}{' '}
|
{t('This market is in auction until it reaches')}{' '}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={<DataGrid grid={compileGridData(market)} />}
|
description={
|
||||||
|
<DataGrid grid={compileGridData(market, market.data)} />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span>{t('sufficient liquidity')}</span>
|
<span>{t('sufficient liquidity')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -300,7 +306,9 @@ export const useOrderValidation = ({
|
|||||||
<span>
|
<span>
|
||||||
{t('This market is in auction due to')}{' '}
|
{t('This market is in auction due to')}{' '}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
description={<DataGrid grid={compileGridData(market)} />}
|
description={
|
||||||
|
<DataGrid grid={compileGridData(market, market.data)} />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span>{t('high price volatility')}</span>
|
<span>{t('high price volatility')}</span>
|
||||||
</Tooltip>
|
</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 type { Candle } from '@vegaprotocol/market-list';
|
||||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
const DEBOUNCE_UPDATE_TIME = 500;
|
const THROTTLE_UPDATE_TIME = 500;
|
||||||
|
|
||||||
export const Last24hVolume = ({
|
export const Last24hVolume = ({
|
||||||
marketId,
|
marketId,
|
||||||
@ -54,7 +54,7 @@ export const Last24hVolume = ({
|
|||||||
const throttledSetCandles = useRef(
|
const throttledSetCandles = useRef(
|
||||||
throttle((data: Candle[]) => {
|
throttle((data: Candle[]) => {
|
||||||
setCandleVolume(calcDayVolume(data));
|
setCandleVolume(calcDayVolume(data));
|
||||||
}, DEBOUNCE_UPDATE_TIME)
|
}, THROTTLE_UPDATE_TIME)
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
@ -78,7 +78,7 @@ export const Last24hVolume = ({
|
|||||||
throttle((candles: Candle[]) => {
|
throttle((candles: Candle[]) => {
|
||||||
const candle24hAgo = candles?.[0];
|
const candle24hAgo = candles?.[0];
|
||||||
setVolumeChange(getChange(data || [], candle24hAgo?.close));
|
setVolumeChange(getChange(data || [], candle24hAgo?.close));
|
||||||
}, DEBOUNCE_UPDATE_TIME)
|
}, THROTTLE_UPDATE_TIME)
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const updateCandle24hAgo = useCallback(
|
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 { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||||
import { Last24hVolume } from '../../components/last-24h-volume';
|
import { Last24hVolume } from '../../components/last-24h-volume';
|
||||||
import { MarketState } from '../../components/market-state';
|
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 { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
||||||
import { MarketState as State } from '@vegaprotocol/types';
|
import { MarketState as State } from '@vegaprotocol/types';
|
||||||
|
|
||||||
@ -64,22 +64,35 @@ export const TradeMarketHeader = ({
|
|||||||
>
|
>
|
||||||
<ExpiryLabel market={market} />
|
<ExpiryLabel market={market} />
|
||||||
</HeaderStat>
|
</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}
|
marketId={market?.id}
|
||||||
decimalPlaces={market?.decimalPlaces}
|
onSelect={onSelect}
|
||||||
isHeader
|
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} />
|
<MarketState market={market} />
|
||||||
{asset ? (
|
{asset ? (
|
||||||
<HeaderStat
|
<HeaderStat
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
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 RISK_ACCEPTED_KEY = 'vega-risk-accepted';
|
||||||
export const MAINNET_WELCOME_HEADER = t(
|
export const MAINNET_WELCOME_HEADER = t(
|
||||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import throttle from 'lodash/throttle';
|
import { useMemo } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import {
|
import {
|
||||||
isNumeric,
|
isNumeric,
|
||||||
t,
|
useThrottledDataProvider,
|
||||||
useDataProvider,
|
|
||||||
useYesterday,
|
useYesterday,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { PriceCellChange } from '@vegaprotocol/ui-toolkit';
|
import { PriceCellChange } from '@vegaprotocol/ui-toolkit';
|
||||||
@ -11,8 +11,7 @@ import * as Schema from '@vegaprotocol/types';
|
|||||||
import type { CandleClose } from '@vegaprotocol/types';
|
import type { CandleClose } from '@vegaprotocol/types';
|
||||||
import type { Candle } from '@vegaprotocol/market-list';
|
import type { Candle } from '@vegaprotocol/market-list';
|
||||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||||
import { HeaderStat } from '../header';
|
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||||
import * as constants from '../constants';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
@ -20,75 +19,48 @@ interface Props {
|
|||||||
initialValue?: string[];
|
initialValue?: string[];
|
||||||
isHeader?: boolean;
|
isHeader?: boolean;
|
||||||
noUpdate?: boolean;
|
noUpdate?: boolean;
|
||||||
|
inViewRoot?: RefObject<Element>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Last24hPriceChange = ({
|
export const Last24hPriceChange = ({
|
||||||
marketId,
|
marketId,
|
||||||
decimalPlaces,
|
decimalPlaces,
|
||||||
initialValue,
|
initialValue,
|
||||||
isHeader = false,
|
inViewRoot,
|
||||||
noUpdate = false,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [candlesClose, setCandlesClose] = useState<string[]>(
|
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||||
initialValue || []
|
|
||||||
);
|
|
||||||
const yesterday = useYesterday();
|
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(
|
const variables = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
marketId: marketId,
|
marketId: marketId,
|
||||||
interval: Schema.Interval.INTERVAL_I1H,
|
interval: Schema.Interval.INTERVAL_I1H,
|
||||||
since: yTimestamp,
|
since: new Date(yesterday).toISOString(),
|
||||||
}),
|
}),
|
||||||
[marketId, yTimestamp]
|
[marketId, yesterday]
|
||||||
);
|
);
|
||||||
|
|
||||||
const throttledSetCandles = useRef(
|
const { data, error } = useThrottledDataProvider<Candle[], Candle>(
|
||||||
throttle((data: Candle[]) => {
|
{
|
||||||
if (!noUpdate) {
|
dataProvider: marketCandlesProvider,
|
||||||
const candlesClose: string[] = data
|
variables,
|
||||||
.map((candle) => candle?.close)
|
skip: !marketId || !inView,
|
||||||
.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;
|
|
||||||
},
|
},
|
||||||
[throttledSetCandles]
|
THROTTLE_UPDATE_TIME
|
||||||
);
|
);
|
||||||
|
|
||||||
const { error } = useDataProvider<Candle[], Candle>({
|
const candles =
|
||||||
dataProvider: marketCandlesProvider,
|
data
|
||||||
update,
|
?.map((candle) => candle?.close)
|
||||||
variables,
|
.filter((c): c is CandleClose => c !== null) || initialValue;
|
||||||
skip: noUpdate || !marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = useMemo(() => {
|
if (error || !isNumeric(decimalPlaces)) {
|
||||||
if (error || !isNumeric(decimalPlaces)) {
|
return <span ref={ref}>-</span>;
|
||||||
return <>-</>;
|
}
|
||||||
}
|
return (
|
||||||
return (
|
<PriceCellChange
|
||||||
<PriceCellChange candles={candlesClose} decimalPlaces={decimalPlaces} />
|
candles={candles || []}
|
||||||
);
|
decimalPlaces={decimalPlaces}
|
||||||
}, [candlesClose, decimalPlaces, error]);
|
ref={ref}
|
||||||
|
/>
|
||||||
return isHeader ? (
|
|
||||||
<HeaderStat heading={t('Change (24h)')} testId="market-change">
|
|
||||||
{content}
|
|
||||||
</HeaderStat>
|
|
||||||
) : (
|
|
||||||
content
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,102 +1,65 @@
|
|||||||
|
import type { RefObject } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import {
|
import {
|
||||||
calcCandleVolume,
|
calcCandleVolume,
|
||||||
marketCandlesProvider,
|
marketCandlesProvider,
|
||||||
} from '@vegaprotocol/market-list';
|
} from '@vegaprotocol/market-list';
|
||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
t,
|
useThrottledDataProvider,
|
||||||
useDataProvider,
|
|
||||||
useYesterday,
|
useYesterday,
|
||||||
isNumeric,
|
isNumeric,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import throttle from 'lodash/throttle';
|
import { useMemo } from 'react';
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
||||||
import * as constants from '../constants';
|
|
||||||
import { HeaderStat } from '../header';
|
|
||||||
import type { Candle } from '@vegaprotocol/market-list';
|
import type { Candle } from '@vegaprotocol/market-list';
|
||||||
|
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
positionDecimalPlaces?: number;
|
positionDecimalPlaces?: number;
|
||||||
noUpdate?: boolean;
|
formatDecimals?: number;
|
||||||
isHeader?: boolean;
|
inViewRoot?: RefObject<Element>;
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Last24hVolume = ({
|
export const Last24hVolume = ({
|
||||||
marketId,
|
marketId,
|
||||||
positionDecimalPlaces,
|
positionDecimalPlaces,
|
||||||
noUpdate = false,
|
formatDecimals,
|
||||||
isHeader = false,
|
inViewRoot,
|
||||||
initialValue,
|
initialValue,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [candleVolume, setCandleVolume] = useState<string>(initialValue || '');
|
|
||||||
const yesterday = useYesterday();
|
const yesterday = useYesterday();
|
||||||
// Cache timestamp for yesterday to prevent full unmount of market page when
|
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||||
// a rerender occurs
|
|
||||||
const yTimestamp = useMemo(() => {
|
|
||||||
return new Date(yesterday).toISOString();
|
|
||||||
}, [yesterday]);
|
|
||||||
|
|
||||||
const variables = useMemo(
|
const variables = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
marketId: marketId,
|
marketId: marketId,
|
||||||
interval: Schema.Interval.INTERVAL_I1H,
|
interval: Schema.Interval.INTERVAL_I1H,
|
||||||
since: yTimestamp,
|
since: new Date(yesterday).toISOString(),
|
||||||
}),
|
}),
|
||||||
[marketId, yTimestamp]
|
[marketId, yesterday]
|
||||||
);
|
);
|
||||||
|
|
||||||
const throttledSetCandles = useRef(
|
const { data } = useThrottledDataProvider<Candle[], Candle>(
|
||||||
throttle((data: Candle[]) => {
|
{
|
||||||
noUpdate || setCandleVolume(calcCandleVolume(data) || '');
|
dataProvider: marketCandlesProvider,
|
||||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
variables,
|
||||||
).current;
|
skip: !(inView && marketId),
|
||||||
const update = useCallback(
|
|
||||||
({ data }: { data: Candle[] | null }) => {
|
|
||||||
if (data) {
|
|
||||||
throttledSetCandles(data);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
[throttledSetCandles]
|
THROTTLE_UPDATE_TIME
|
||||||
);
|
);
|
||||||
|
const candleVolume = data ? calcCandleVolume(data) : initialValue;
|
||||||
const { error } = useDataProvider<Candle[], Candle>({
|
return (
|
||||||
dataProvider: marketCandlesProvider,
|
<span ref={ref}>
|
||||||
update,
|
{candleVolume && isNumeric(positionDecimalPlaces)
|
||||||
variables,
|
? addDecimalsFormatNumber(
|
||||||
skip: noUpdate || !marketId,
|
candleVolume,
|
||||||
});
|
positionDecimalPlaces,
|
||||||
|
formatDecimals
|
||||||
const formatDecimals = isHeader ? positionDecimalPlaces || 0 : 2;
|
)
|
||||||
const content = useMemo(() => {
|
: '-'}
|
||||||
return (
|
</span>
|
||||||
<>
|
|
||||||
{!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
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import throttle from 'lodash/throttle';
|
import { useMemo } from 'react';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
t,
|
|
||||||
PriceCell,
|
PriceCell,
|
||||||
useDataProvider,
|
useThrottledDataProvider,
|
||||||
isNumeric,
|
isNumeric,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import type {
|
import type {
|
||||||
@ -12,14 +12,13 @@ import type {
|
|||||||
MarketDataUpdateFieldsFragment,
|
MarketDataUpdateFieldsFragment,
|
||||||
} from '@vegaprotocol/market-list';
|
} from '@vegaprotocol/market-list';
|
||||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||||
import { HeaderStat } from '../header';
|
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||||
import * as constants from '../constants';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
decimalPlaces?: number;
|
decimalPlaces?: number;
|
||||||
isHeader?: boolean;
|
asPriceCell?: boolean;
|
||||||
noUpdate?: boolean;
|
inViewRoot?: RefObject<Element>;
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,58 +26,39 @@ export const MarketMarkPrice = ({
|
|||||||
marketId,
|
marketId,
|
||||||
decimalPlaces,
|
decimalPlaces,
|
||||||
initialValue,
|
initialValue,
|
||||||
isHeader = false,
|
inViewRoot,
|
||||||
noUpdate = false,
|
asPriceCell,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [marketPrice, setMarketPrice] = useState<string | null>(
|
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||||
initialValue || null
|
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||||
);
|
|
||||||
const variables = useMemo(
|
|
||||||
() => ({
|
|
||||||
marketId: marketId,
|
|
||||||
}),
|
|
||||||
[marketId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const throttledSetMarketPrice = useRef(
|
const { data } = useThrottledDataProvider<
|
||||||
throttle((price: string) => {
|
MarketData,
|
||||||
noUpdate || setMarketPrice(price);
|
MarketDataUpdateFieldsFragment
|
||||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
>(
|
||||||
).current;
|
{
|
||||||
const update = useCallback(
|
dataProvider: marketDataProvider,
|
||||||
({ data: marketData }: { data: MarketData | null }) => {
|
variables,
|
||||||
throttledSetMarketPrice(marketData?.markPrice || '');
|
skip: !inView,
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
[throttledSetMarketPrice]
|
THROTTLE_UPDATE_TIME
|
||||||
);
|
);
|
||||||
|
|
||||||
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
|
const marketPrice = data?.markPrice || initialValue;
|
||||||
dataProvider: marketDataProvider,
|
|
||||||
update,
|
|
||||||
variables,
|
|
||||||
skip: noUpdate || !marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = useMemo(() => {
|
if (!marketPrice || !isNumeric(decimalPlaces)) {
|
||||||
if (!marketPrice || !isNumeric(decimalPlaces)) {
|
return <span ref={ref}>-</span>;
|
||||||
return <>-</>;
|
}
|
||||||
}
|
if (asPriceCell) {
|
||||||
return isHeader ? (
|
return (
|
||||||
<div>{addDecimalsFormatNumber(marketPrice, decimalPlaces)}</div>
|
|
||||||
) : (
|
|
||||||
<PriceCell
|
<PriceCell
|
||||||
|
ref={ref}
|
||||||
value={Number(marketPrice)}
|
value={Number(marketPrice)}
|
||||||
valueFormatted={addDecimalsFormatNumber(marketPrice, decimalPlaces, 2)}
|
valueFormatted={addDecimalsFormatNumber(marketPrice, decimalPlaces, 2)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [marketPrice, decimalPlaces, isHeader]);
|
}
|
||||||
|
return (
|
||||||
return isHeader ? (
|
<span ref={ref}>{addDecimalsFormatNumber(marketPrice, decimalPlaces)}</span>
|
||||||
<HeaderStat heading={t('Price')} testId="market-price">
|
|
||||||
{content}
|
|
||||||
</HeaderStat>
|
|
||||||
) : (
|
|
||||||
content
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export const MarketState = ({
|
|||||||
const throttledSetMarketState = useRef(
|
const throttledSetMarketState = useRef(
|
||||||
throttle((state: Schema.MarketState) => {
|
throttle((state: Schema.MarketState) => {
|
||||||
setMarketState(state);
|
setMarketState(state);
|
||||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
}, constants.THROTTLE_UPDATE_TIME)
|
||||||
).current;
|
).current;
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
|
|||||||
@ -1,110 +1,78 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
import { TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||||
import { compileGridData, TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import type {
|
import { useStaticMarketData } from '@vegaprotocol/market-list';
|
||||||
MarketData,
|
|
||||||
MarketDataUpdateFieldsFragment,
|
|
||||||
SingleMarketFieldsFragment,
|
|
||||||
} from '@vegaprotocol/market-list';
|
|
||||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
|
||||||
import { HeaderStat } from '../header';
|
import { HeaderStat } from '../header';
|
||||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface Props {
|
const getTradingModeLabel = (
|
||||||
marketId?: string;
|
tradingMode?: Schema.MarketTradingMode,
|
||||||
onSelect?: (marketId: string) => void;
|
trigger?: Schema.AuctionTrigger
|
||||||
isHeader?: boolean;
|
) => {
|
||||||
noUpdate?: boolean;
|
return (
|
||||||
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 =
|
|
||||||
(tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
(tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||||
trigger &&
|
trigger &&
|
||||||
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
||||||
? `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`
|
? `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`
|
||||||
: Schema.MarketTradingModeMapping[
|
: Schema.MarketTradingModeMapping[
|
||||||
tradingMode as Schema.MarketTradingMode
|
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
|
<HeaderStat
|
||||||
heading={t('Trading mode')}
|
heading={t('Trading mode')}
|
||||||
description={
|
description={
|
||||||
market && (
|
<TradingModeTooltip marketId={marketId} onSelect={onSelect} />
|
||||||
<TradingModeTooltip
|
|
||||||
tradingMode={tradingMode}
|
|
||||||
trigger={trigger}
|
|
||||||
compiledGrid={compileGridData(market, onSelect)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
testId="market-trading-mode"
|
testId="market-trading-mode"
|
||||||
>
|
>
|
||||||
<div>{content}</div>
|
<div>{getTradingModeLabel(tradingMode, trigger)}</div>
|
||||||
</HeaderStat>
|
</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
|
<Tooltip
|
||||||
description={
|
description={<TradingModeTooltip marketId={marketId} skip={!inView} />}
|
||||||
tradingMode &&
|
|
||||||
trigger && (
|
|
||||||
<TradingModeTooltip tradingMode={tradingMode} trigger={trigger} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<span>{content}</span>
|
<span ref={ref}>
|
||||||
|
{getTradingModeLabel(
|
||||||
|
data?.marketTradingMode ?? initialTradingMode,
|
||||||
|
data?.trigger ?? initialTrigger
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export const MarketVolume = ({ marketId }: { marketId: string }) => {
|
|||||||
const throttledSetMarketVolume = useRef(
|
const throttledSetMarketVolume = useRef(
|
||||||
throttle((volume: string) => {
|
throttle((volume: string) => {
|
||||||
setMarketVolume(volume);
|
setMarketVolume(volume);
|
||||||
}, constants.DEBOUNCE_UPDATE_TIME)
|
}, constants.THROTTLE_UPDATE_TIME)
|
||||||
).current;
|
).current;
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
({ data: marketData }: { data: MarketData | null }) => {
|
({ data: marketData }: { data: MarketData | null }) => {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { RefObject } from 'react';
|
||||||
import { FeesCell } from '@vegaprotocol/market-info';
|
import { FeesCell } from '@vegaprotocol/market-info';
|
||||||
import {
|
import {
|
||||||
calcCandleHigh,
|
calcCandleHigh,
|
||||||
@ -173,7 +174,7 @@ export const columns = (
|
|||||||
market: Market,
|
market: Market,
|
||||||
onSelect: (id: string) => void,
|
onSelect: (id: string) => void,
|
||||||
onCellClick: OnCellClickHandler,
|
onCellClick: OnCellClickHandler,
|
||||||
activeMarketId?: string | null
|
inViewRoot?: RefObject<HTMLElement>
|
||||||
) => {
|
) => {
|
||||||
const candlesClose = market.candles
|
const candlesClose = market.candles
|
||||||
?.map((candle) => candle?.close)
|
?.map((candle) => candle?.close)
|
||||||
@ -189,7 +190,6 @@ export const columns = (
|
|||||||
return onSelect(id);
|
return onSelect(id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const noUpdate = !activeMarketId || market.id !== activeMarketId;
|
|
||||||
const selectMarketColumns: Column[] = [
|
const selectMarketColumns: Column[] = [
|
||||||
{
|
{
|
||||||
kind: ColumnKind.Market,
|
kind: ColumnKind.Market,
|
||||||
@ -221,8 +221,9 @@ export const columns = (
|
|||||||
<MarketMarkPrice
|
<MarketMarkPrice
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market?.decimalPlaces}
|
decimalPlaces={market?.decimalPlaces}
|
||||||
initialValue={market.data?.markPrice.toString()}
|
initialValue={market.data?.markPrice}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
|
asPriceCell
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
className: `${cellClassNames} max-w-[100px]`,
|
className: `${cellClassNames} max-w-[100px]`,
|
||||||
@ -234,7 +235,7 @@ export const columns = (
|
|||||||
<Last24hPriceChange
|
<Last24hPriceChange
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market?.decimalPlaces}
|
decimalPlaces={market?.decimalPlaces}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
initialValue={candlesClose}
|
initialValue={candlesClose}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -317,7 +318,8 @@ export const columns = (
|
|||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||||
initialValue={candleVolume}
|
initialValue={candleVolume}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
|
formatDecimals={2}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||||
@ -329,8 +331,8 @@ export const columns = (
|
|||||||
value: (
|
value: (
|
||||||
<MarketTradingMode
|
<MarketTradingMode
|
||||||
marketId={market?.id}
|
marketId={market?.id}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
initialMode={market.tradingMode}
|
initialTradingMode={market.tradingMode}
|
||||||
initialTrigger={market.data?.trigger}
|
initialTrigger={market.data?.trigger}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -359,9 +361,9 @@ export const columns = (
|
|||||||
export const columnsPositionMarkets = (
|
export const columnsPositionMarkets = (
|
||||||
market: Market,
|
market: Market,
|
||||||
onSelect: (id: string) => void,
|
onSelect: (id: string) => void,
|
||||||
|
inViewRoot?: RefObject<HTMLElement>,
|
||||||
openVolume?: string,
|
openVolume?: string,
|
||||||
onCellClick?: OnCellClickHandler,
|
onCellClick?: OnCellClickHandler
|
||||||
activeMarketId?: string | null
|
|
||||||
) => {
|
) => {
|
||||||
const candlesClose = market.candles
|
const candlesClose = market.candles
|
||||||
?.map((candle) => candle?.close)
|
?.map((candle) => candle?.close)
|
||||||
@ -377,7 +379,6 @@ export const columnsPositionMarkets = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
||||||
const noUpdate = !activeMarketId || market.id !== activeMarketId;
|
|
||||||
const selectMarketColumns: Column[] = [
|
const selectMarketColumns: Column[] = [
|
||||||
{
|
{
|
||||||
kind: ColumnKind.Market,
|
kind: ColumnKind.Market,
|
||||||
@ -409,8 +410,9 @@ export const columnsPositionMarkets = (
|
|||||||
<MarketMarkPrice
|
<MarketMarkPrice
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market?.decimalPlaces}
|
decimalPlaces={market?.decimalPlaces}
|
||||||
initialValue={market.data?.markPrice.toString()}
|
inViewRoot={inViewRoot}
|
||||||
noUpdate={noUpdate}
|
initialValue={market.data?.markPrice}
|
||||||
|
asPriceCell
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
className: cellClassNames,
|
className: cellClassNames,
|
||||||
@ -422,7 +424,7 @@ export const columnsPositionMarkets = (
|
|||||||
<Last24hPriceChange
|
<Last24hPriceChange
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market?.decimalPlaces}
|
decimalPlaces={market?.decimalPlaces}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
initialValue={candlesClose}
|
initialValue={candlesClose}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -503,9 +505,10 @@ export const columnsPositionMarkets = (
|
|||||||
value: (
|
value: (
|
||||||
<Last24hVolume
|
<Last24hVolume
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
|
inViewRoot={inViewRoot}
|
||||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||||
initialValue={candleVolume}
|
initialValue={candleVolume}
|
||||||
noUpdate={noUpdate}
|
formatDecimals={2}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
className: `${cellClassNames} hidden lg:table-cell font-mono`,
|
||||||
@ -517,8 +520,8 @@ export const columnsPositionMarkets = (
|
|||||||
value: (
|
value: (
|
||||||
<MarketTradingMode
|
<MarketTradingMode
|
||||||
marketId={market?.id}
|
marketId={market?.id}
|
||||||
noUpdate={noUpdate}
|
inViewRoot={inViewRoot}
|
||||||
initialMode={market.tradingMode}
|
initialTradingMode={market.tradingMode}
|
||||||
initialTrigger={market.data?.trigger}
|
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 { useMarketList } from '@vegaprotocol/market-list';
|
||||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
@ -27,7 +28,6 @@ import {
|
|||||||
TOKEN_NEW_MARKET_PROPOSAL,
|
TOKEN_NEW_MARKET_PROPOSAL,
|
||||||
useLinks,
|
useLinks,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { useGlobalStore } from '../../stores';
|
|
||||||
|
|
||||||
export type Market = MarketWithCandles & MarketWithData;
|
export type Market = MarketWithCandles & MarketWithData;
|
||||||
|
|
||||||
@ -36,19 +36,22 @@ export const SelectAllMarketsTableBody = ({
|
|||||||
positions,
|
positions,
|
||||||
onSelect,
|
onSelect,
|
||||||
onCellClick,
|
onCellClick,
|
||||||
activeMarketId,
|
inViewRoot,
|
||||||
headers = columnHeaders,
|
headers = columnHeaders,
|
||||||
tableColumns = (market) =>
|
tableColumns = (market) => columns(market, onSelect, onCellClick, inViewRoot),
|
||||||
columns(market, onSelect, onCellClick, activeMarketId),
|
|
||||||
}: {
|
}: {
|
||||||
markets?: Market[] | null;
|
markets?: Market[] | null;
|
||||||
positions?: PositionFieldsFragment[];
|
positions?: PositionFieldsFragment[];
|
||||||
title?: string;
|
title?: string;
|
||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
onCellClick: OnCellClickHandler;
|
onCellClick: OnCellClickHandler;
|
||||||
activeMarketId?: string | null;
|
|
||||||
headers?: Column[];
|
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);
|
const tokenLink = useLinks(DApp.Token);
|
||||||
if (!markets) return null;
|
if (!markets) return null;
|
||||||
@ -68,6 +71,7 @@ export const SelectAllMarketsTableBody = ({
|
|||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
columns={tableColumns(
|
columns={tableColumns(
|
||||||
market,
|
market,
|
||||||
|
inViewRoot,
|
||||||
positions &&
|
positions &&
|
||||||
positions.find((p) => p.market.id === market.id)?.openVolume
|
positions.find((p) => p.market.id === market.id)?.openVolume
|
||||||
)}
|
)}
|
||||||
@ -95,11 +99,11 @@ export const SelectMarketPopover = ({
|
|||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
onCellClick: OnCellClickHandler;
|
onCellClick: OnCellClickHandler;
|
||||||
}) => {
|
}) => {
|
||||||
const activeMarketId = useGlobalStore((store) => store.marketId);
|
|
||||||
const triggerClasses =
|
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';
|
'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 { pubKey } = useVegaWallet();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const inViewRoot = useRef<HTMLDivElement>(null);
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
loading: marketsLoading,
|
loading: marketsLoading,
|
||||||
@ -155,6 +159,7 @@ export const SelectMarketPopover = ({
|
|||||||
<div
|
<div
|
||||||
className="w-[90vw] max-h-[80vh] overflow-y-auto"
|
className="w-[90vw] max-h-[80vh] overflow-y-auto"
|
||||||
data-testid="select-market-list"
|
data-testid="select-market-list"
|
||||||
|
ref={inViewRoot}
|
||||||
>
|
>
|
||||||
{marketsLoading || (pubKey && positionsLoading) ? (
|
{marketsLoading || (pubKey && positionsLoading) ? (
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
@ -167,6 +172,7 @@ export const SelectMarketPopover = ({
|
|||||||
<>
|
<>
|
||||||
<TableTitle>{t('My markets')}</TableTitle>
|
<TableTitle>{t('My markets')}</TableTitle>
|
||||||
<SelectAllMarketsTableBody
|
<SelectAllMarketsTableBody
|
||||||
|
inViewRoot={inViewRoot}
|
||||||
markets={markets}
|
markets={markets}
|
||||||
positions={party?.positionsConnection?.edges
|
positions={party?.positionsConnection?.edges
|
||||||
?.filter((edge) => edge.node)
|
?.filter((edge) => edge.node)
|
||||||
@ -174,13 +180,13 @@ export const SelectMarketPopover = ({
|
|||||||
onSelect={onSelectMarket}
|
onSelect={onSelectMarket}
|
||||||
onCellClick={onCellClick}
|
onCellClick={onCellClick}
|
||||||
headers={columnHeadersPositionMarkets}
|
headers={columnHeadersPositionMarkets}
|
||||||
tableColumns={(market, openVolume) =>
|
tableColumns={(market, inViewRoot, openVolume) =>
|
||||||
columnsPositionMarkets(
|
columnsPositionMarkets(
|
||||||
market,
|
market,
|
||||||
onSelectMarket,
|
onSelectMarket,
|
||||||
|
inViewRoot,
|
||||||
openVolume,
|
openVolume,
|
||||||
onCellClick,
|
onCellClick
|
||||||
activeMarketId
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -188,10 +194,10 @@ export const SelectMarketPopover = ({
|
|||||||
) : null}
|
) : null}
|
||||||
<TableTitle>{t('All markets')}</TableTitle>
|
<TableTitle>{t('All markets')}</TableTitle>
|
||||||
<SelectAllMarketsTableBody
|
<SelectAllMarketsTableBody
|
||||||
|
inViewRoot={inViewRoot}
|
||||||
markets={data}
|
markets={data}
|
||||||
onSelect={onSelectMarket}
|
onSelect={onSelectMarket}
|
||||||
onCellClick={onCellClick}
|
onCellClick={onCellClick}
|
||||||
activeMarketId={activeMarketId}
|
|
||||||
/>
|
/>
|
||||||
</table>
|
</table>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import 'jest-canvas-mock';
|
import 'jest-canvas-mock';
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
|
import { defaultFallbackInView } from 'react-intersection-observer';
|
||||||
|
|
||||||
|
defaultFallbackInView(true);
|
||||||
global.ResizeObserver = ResizeObserver;
|
global.ResizeObserver = ResizeObserver;
|
||||||
|
|
||||||
// Required by radix-ui/react-tooltip
|
// Required by radix-ui/react-tooltip
|
||||||
|
|||||||
@ -78,7 +78,11 @@ export const TimeInForceSelector = ({
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{t('This market is in auction until it reaches')}{' '}
|
{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>
|
<span>{t('sufficient liquidity')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{'. '}
|
{'. '}
|
||||||
@ -93,7 +97,11 @@ export const TimeInForceSelector = ({
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{t('This market is in auction due to')}{' '}
|
{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>
|
<span>{t('high price volatility')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{'. '}
|
{'. '}
|
||||||
|
|||||||
@ -33,7 +33,11 @@ export const TypeSelector = ({
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{t('This market is in auction until it reaches')}{' '}
|
{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>
|
<span>{t('sufficient liquidity')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{'. '}
|
{'. '}
|
||||||
@ -46,7 +50,11 @@ export const TypeSelector = ({
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{t('This market is in auction due to')}{' '}
|
{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>
|
<span>{t('high price volatility')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{'. '}
|
{'. '}
|
||||||
|
|||||||
@ -8,17 +8,31 @@ import * as Schema from '@vegaprotocol/types';
|
|||||||
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
|
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
import type { Market, MarketData } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
export const compileGridData = (
|
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
|
onSelect?: (id: string) => void
|
||||||
): { label: ReactNode; value?: ReactNode }[] => {
|
): { label: ReactNode; value?: ReactNode }[] => {
|
||||||
const grid: DataGridProps['grid'] = [];
|
const grid: DataGridProps['grid'] = [];
|
||||||
const isLiquidityMonitoringAuction =
|
const isLiquidityMonitoringAuction =
|
||||||
market.data.marketTradingMode ===
|
marketData.marketTradingMode ===
|
||||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
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 formatStake = (value: string) => {
|
||||||
const formattedValue = addDecimalsFormatNumber(
|
const formattedValue = addDecimalsFormatNumber(
|
||||||
@ -30,19 +44,17 @@ export const compileGridData = (
|
|||||||
return `${formattedValue} ${asset}`;
|
return `${formattedValue} ${asset}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!market.data) return grid;
|
if (!marketData) return grid;
|
||||||
|
|
||||||
if (market.data.auctionStart) {
|
if (marketData.auctionStart) {
|
||||||
grid.push({
|
grid.push({
|
||||||
label: t('Auction start'),
|
label: t('Auction start'),
|
||||||
value: getDateTimeFormat().format(new Date(market.data.auctionStart)),
|
value: getDateTimeFormat().format(new Date(marketData.auctionStart)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (market.data.auctionEnd) {
|
if (marketData.auctionEnd) {
|
||||||
const endDate = getDateTimeFormat().format(
|
const endDate = getDateTimeFormat().format(new Date(marketData.auctionEnd));
|
||||||
new Date(market.data.auctionEnd)
|
|
||||||
);
|
|
||||||
grid.push({
|
grid.push({
|
||||||
label: isLiquidityMonitoringAuction
|
label: isLiquidityMonitoringAuction
|
||||||
? t('Est. auction end')
|
? t('Est. auction end')
|
||||||
@ -51,14 +63,14 @@ export const compileGridData = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLiquidityMonitoringAuction && market.data.targetStake) {
|
if (isLiquidityMonitoringAuction && marketData.targetStake) {
|
||||||
grid.push({
|
grid.push({
|
||||||
label: t('Target liquidity'),
|
label: t('Target liquidity'),
|
||||||
value: formatStake(market.data.targetStake),
|
value: formatStake(marketData.targetStake),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLiquidityMonitoringAuction && market.data.suppliedStake) {
|
if (isLiquidityMonitoringAuction && marketData.suppliedStake) {
|
||||||
grid.push({
|
grid.push({
|
||||||
label: (
|
label: (
|
||||||
<Link
|
<Link
|
||||||
@ -68,31 +80,31 @@ export const compileGridData = (
|
|||||||
<UILink>{t('Current liquidity')}</UILink>
|
<UILink>{t('Current liquidity')}</UILink>
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
value: formatStake(market.data.suppliedStake),
|
value: formatStake(marketData.suppliedStake),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (market.data.indicativePrice) {
|
if (marketData.indicativePrice) {
|
||||||
grid.push({
|
grid.push({
|
||||||
label: t('Est. uncrossing price'),
|
label: t('Est. uncrossing price'),
|
||||||
value:
|
value:
|
||||||
market.data.indicativePrice && market.data.indicativePrice !== '0'
|
marketData.indicativePrice && marketData.indicativePrice !== '0'
|
||||||
? `~
|
? `~
|
||||||
${addDecimalsFormatNumber(
|
${addDecimalsFormatNumber(
|
||||||
market.data.indicativePrice,
|
marketData.indicativePrice,
|
||||||
market.decimalPlaces
|
market.decimalPlaces
|
||||||
)}`
|
)}`
|
||||||
: '-',
|
: '-',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (market.data.indicativeVolume) {
|
if (marketData.indicativeVolume) {
|
||||||
grid.push({
|
grid.push({
|
||||||
label: t('Est. uncrossing vol'),
|
label: t('Est. uncrossing vol'),
|
||||||
value:
|
value:
|
||||||
market.data.indicativeVolume && market.data.indicativeVolume !== '0'
|
marketData.indicativeVolume && marketData.indicativeVolume !== '0'
|
||||||
? '~' +
|
? '~' +
|
||||||
addDecimalsFormatNumber(
|
addDecimalsFormatNumber(
|
||||||
market.data.indicativeVolume,
|
marketData.indicativeVolume,
|
||||||
market.positionDecimalPlaces
|
market.positionDecimalPlaces
|
||||||
)
|
)
|
||||||
: '-',
|
: '-',
|
||||||
|
|||||||
@ -1,23 +1,35 @@
|
|||||||
import type { ReactNode } from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { DataGrid, t } from '@vegaprotocol/react-helpers';
|
import { DataGrid, t } from '@vegaprotocol/react-helpers';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { createDocsLinks } from '@vegaprotocol/react-helpers';
|
import { createDocsLinks } from '@vegaprotocol/react-helpers';
|
||||||
|
import { compileGridData } from './compile-grid-data';
|
||||||
|
import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
|
||||||
|
|
||||||
type TradingModeTooltipProps = {
|
type TradingModeTooltipProps = {
|
||||||
tradingMode: Schema.MarketTradingMode | null;
|
marketId?: string;
|
||||||
trigger: Schema.AuctionTrigger | null;
|
onSelect?: (marketId: string) => void;
|
||||||
compiledGrid?: { label: ReactNode; value?: ReactNode }[];
|
skip?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TradingModeTooltip = ({
|
export const TradingModeTooltip = ({
|
||||||
tradingMode,
|
marketId,
|
||||||
trigger,
|
onSelect,
|
||||||
compiledGrid,
|
skip,
|
||||||
}: TradingModeTooltipProps) => {
|
}: TradingModeTooltipProps) => {
|
||||||
const { VEGA_DOCS_URL } = useEnvironment();
|
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) {
|
switch (tradingMode) {
|
||||||
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
makeDerivedDataProvider,
|
||||||
|
useDataProvider,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
MarketDataDocument,
|
MarketDataDocument,
|
||||||
@ -39,3 +44,51 @@ export const marketDataProvider = makeDataProvider<
|
|||||||
getData,
|
getData,
|
||||||
getDelta,
|
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',
|
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>(
|
export const activeMarketsProvider = makeDerivedDataProvider<Market[], never>(
|
||||||
[marketsProvider],
|
[marketsProvider],
|
||||||
([markets]) => filterAndSortMarkets(markets)
|
([markets]) => filterAndSortMarkets(markets)
|
||||||
|
|||||||
@ -86,6 +86,8 @@ describe('isNumeric', () => {
|
|||||||
{ i: '--123.01', o: false },
|
{ i: '--123.01', o: false },
|
||||||
{ i: '123.', o: false },
|
{ i: '123.', o: false },
|
||||||
{ i: '123.1.1', 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), o: true },
|
||||||
{ i: new BigNumber(123.123), o: true },
|
{ i: new BigNumber(123.123), o: true },
|
||||||
{ i: new BigNumber(123.123).toString(), o: true },
|
{ i: new BigNumber(123.123).toString(), o: true },
|
||||||
@ -98,7 +100,7 @@ describe('isNumeric', () => {
|
|||||||
i,
|
i,
|
||||||
o,
|
o,
|
||||||
}: {
|
}: {
|
||||||
i: number | string | undefined | null | BigNumber;
|
i: number | string | undefined | null | BigNumber | bigint;
|
||||||
o: boolean;
|
o: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
expect(isNumeric(i)).toStrictEqual(o);
|
expect(isNumeric(i)).toStrictEqual(o);
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export const useNumberParts = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isNumeric = (
|
export const isNumeric = (
|
||||||
value?: string | number | BigNumber | null
|
value?: string | number | BigNumber | bigint | null
|
||||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||||
|
|
||||||
const INFINITY = '∞';
|
const INFINITY = '∞';
|
||||||
|
|||||||
@ -560,7 +560,8 @@ export type CombineDerivedData<
|
|||||||
Variables extends OperationVariables = OperationVariables
|
Variables extends OperationVariables = OperationVariables
|
||||||
> = (
|
> = (
|
||||||
data: DerivedPart<Variables>['data'][],
|
data: DerivedPart<Variables>['data'][],
|
||||||
variables?: Variables
|
variables: Variables | undefined,
|
||||||
|
prevData: Data | null
|
||||||
) => Data | null;
|
) => Data | null;
|
||||||
|
|
||||||
export type CombineDerivedDelta<
|
export type CombineDerivedDelta<
|
||||||
@ -641,7 +642,8 @@ function makeDerivedDataProviderInternal<
|
|||||||
const newData = newLoaded
|
const newData = newLoaded
|
||||||
? combineData(
|
? combineData(
|
||||||
parts.map((part) => part.data),
|
parts.map((part) => part.data),
|
||||||
variables
|
variables,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
: data;
|
: data;
|
||||||
if (
|
if (
|
||||||
@ -655,7 +657,7 @@ function makeDerivedDataProviderInternal<
|
|||||||
loaded = newLoaded;
|
loaded = newLoaded;
|
||||||
const previousData = data;
|
const previousData = data;
|
||||||
data = newData;
|
data = newData;
|
||||||
if (newLoaded) {
|
if (loaded) {
|
||||||
const updatedPart = parts[updatedPartIndex];
|
const updatedPart = parts[updatedPartIndex];
|
||||||
if (updatedPart.isUpdate) {
|
if (updatedPart.isUpdate) {
|
||||||
isUpdate = true;
|
isUpdate = true;
|
||||||
|
|||||||
@ -1,37 +1,41 @@
|
|||||||
import React from 'react';
|
import { memo, forwardRef } from 'react';
|
||||||
import { getDecimalSeparator } from '../format';
|
import { getDecimalSeparator, isNumeric } from '../format';
|
||||||
export interface IPriceCellProps {
|
export interface IPriceCellProps {
|
||||||
value: number | bigint | null | undefined;
|
value: number | bigint | null | undefined;
|
||||||
valueFormatted: string;
|
valueFormatted: string;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PriceCell = React.memo(
|
export const PriceCell = memo(
|
||||||
({ value, valueFormatted, testId }: IPriceCellProps) => {
|
forwardRef<HTMLSpanElement, IPriceCellProps>(
|
||||||
if (
|
({ value, valueFormatted, testId }: IPriceCellProps, ref) => {
|
||||||
(!value && value !== 0) ||
|
if (!isNumeric(value)) {
|
||||||
(typeof value === 'number' && isNaN(Number(value)))
|
return (
|
||||||
) {
|
<span data-testid="price" ref={ref}>
|
||||||
return <span data-testid="price">-</span>;
|
-
|
||||||
|
</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';
|
PriceCell.displayName = 'PriceCell';
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import React from 'react';
|
import { memo, forwardRef } from 'react';
|
||||||
import { signedNumberCssClass } from '@vegaprotocol/react-helpers';
|
import { signedNumberCssClass } from '@vegaprotocol/react-helpers';
|
||||||
import { Arrow } from '../arrows/arrow';
|
import { Arrow } from '../arrows/arrow';
|
||||||
|
|
||||||
@ -36,30 +36,33 @@ export const priceChange = (candles: string[]) => {
|
|||||||
: 0;
|
: 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PriceCellChange = React.memo(
|
export const PriceCellChange = memo(
|
||||||
({ candles, decimalPlaces }: PriceChangeCellProps) => {
|
forwardRef<HTMLSpanElement, PriceChangeCellProps>(
|
||||||
const change = priceChange(candles);
|
({ candles, decimalPlaces }: PriceChangeCellProps, ref) => {
|
||||||
const changePercentage = priceChangePercentage(candles);
|
const change = priceChange(candles);
|
||||||
return (
|
const changePercentage = priceChangePercentage(candles);
|
||||||
<span
|
return (
|
||||||
className={`${signedNumberCssClass(
|
<span
|
||||||
change
|
ref={ref}
|
||||||
)} flex items-center gap-2 justify-end font-mono text-ui-small`}
|
className={`${signedNumberCssClass(
|
||||||
>
|
change
|
||||||
<Arrow value={change} />
|
)} flex items-center gap-2 justify-end font-mono text-ui-small`}
|
||||||
<span data-testid="price-change-percentage">
|
>
|
||||||
{formatNumberPercentage(
|
<Arrow value={change} />
|
||||||
new BigNumber(changePercentage.toString()),
|
<span data-testid="price-change-percentage">
|
||||||
2
|
{formatNumberPercentage(
|
||||||
)}
|
new BigNumber(changePercentage.toString()),
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<span data-testid="price-change">
|
||||||
|
{addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span data-testid="price-change">
|
);
|
||||||
{addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
|
}
|
||||||
</span>
|
)
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
PriceCellChange.displayName = 'PriceCellChange';
|
PriceCellChange.displayName = 'PriceCellChange';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user