fix(trading): refactor market info accordion to avoid remount on candle reload (#3447)
This commit is contained in:
parent
460ccdb3a2
commit
c15051d457
2
.github/workflows/ci-cd-trigger.yml
vendored
2
.github/workflows/ci-cd-trigger.yml
vendored
@ -50,7 +50,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
lint-test-build:
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
needs: node-modules
|
||||
runs-on: ubuntu-22.04
|
||||
name: '(CI) lint + unit test + build'
|
||||
|
@ -60,9 +60,11 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
<>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
data={trigger}
|
||||
data={{
|
||||
maxValidPrice: trigger.maxValidPrice,
|
||||
minValidPrice: trigger.minValidPrice,
|
||||
}}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
omits={['referencePrice', '__typename']}
|
||||
/>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
|
@ -108,18 +108,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
|
||||
it('risk model displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Risk model').click();
|
||||
|
||||
validateMarketDataRow(0, 'Typename', 'LogNormalRiskModel');
|
||||
validateMarketDataRow(1, 'Tau', '0.0001140771161');
|
||||
validateMarketDataRow(2, 'Risk Aversion Parameter', '0.01');
|
||||
validateMarketDataRow(0, 'Tau', '0.0001140771161');
|
||||
validateMarketDataRow(1, 'Risk Aversion Parameter', '0.01');
|
||||
});
|
||||
|
||||
it('risk parameters displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Risk parameters').click();
|
||||
|
||||
validateMarketDataRow(0, 'Typename', 'LogNormalModelParams');
|
||||
validateMarketDataRow(1, 'R', '0.016');
|
||||
validateMarketDataRow(2, 'Sigma', '0.3');
|
||||
validateMarketDataRow(0, 'R', '0.016');
|
||||
validateMarketDataRow(1, 'Sigma', '0.3');
|
||||
});
|
||||
|
||||
it('risk factors displayed', () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||
import { MarketInfoContainer } from '@vegaprotocol/market-info';
|
||||
import { MarketInfoAccordionContainer } from '@vegaprotocol/market-info';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
@ -38,7 +38,7 @@ type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
| typeof DepthChartContainer
|
||||
| typeof DealTicketContainer
|
||||
| typeof MarketInfoContainer
|
||||
| typeof MarketInfoAccordionContainer
|
||||
| typeof OrderbookContainer
|
||||
| typeof TradesContainer;
|
||||
|
||||
@ -56,7 +56,7 @@ const TradingViews = {
|
||||
Depth: requiresMarket(DepthChartContainer),
|
||||
Liquidity: requiresMarket(LiquidityContainer),
|
||||
Ticket: requiresMarket(DealTicketContainer),
|
||||
Info: requiresMarket(MarketInfoContainer),
|
||||
Info: requiresMarket(MarketInfoAccordionContainer),
|
||||
Orderbook: requiresMarket(OrderbookContainer),
|
||||
Trades: requiresMarket(TradesContainer),
|
||||
Positions: PositionsContainer,
|
||||
|
@ -13,8 +13,7 @@ import type { OnCellClickHandler } from '../../components/select-market';
|
||||
import { Header, HeaderStat } from '../../components/header';
|
||||
import { NO_MARKET } from './constants';
|
||||
import { MarketMarkPrice } from '../../components/market-mark-price';
|
||||
import { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||
import { Last24hVolume } from '../../components/last-24h-volume';
|
||||
import { Last24hPriceChange, Last24hVolume } from '@vegaprotocol/market-info';
|
||||
import { MarketState } from '../../components/market-state';
|
||||
import { HeaderStatMarketTradingMode } from '../../components/market-trading-mode';
|
||||
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
||||
|
@ -14,9 +14,8 @@ import type { CandleClose } from '@vegaprotocol/types';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/market-list';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MarketMarkPrice } from '../market-mark-price';
|
||||
import { Last24hPriceChange } from '../last-24h-price-change';
|
||||
import { Last24hPriceChange, Last24hVolume } from '@vegaprotocol/market-info';
|
||||
import { MarketTradingMode } from '../market-trading-mode';
|
||||
import { Last24hVolume } from '../last-24h-volume';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
const ellipsisClasses = 'whitespace-nowrap overflow-hidden text-ellipsis';
|
||||
|
@ -16,19 +16,18 @@ export const useInitialMargin = (
|
||||
order?: OrderSubmissionBody['orderSubmission']
|
||||
) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const commonVariables = { marketId, partyId: pubKey || '' };
|
||||
const { data: marketData } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId },
|
||||
});
|
||||
const { data: activeVolumeAndMargin } = useDataProvider({
|
||||
dataProvider: volumeAndMarginProvider,
|
||||
variables: commonVariables,
|
||||
variables: { marketId, partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { data: marketInfo } = useDataProvider({
|
||||
dataProvider: marketInfoProvider,
|
||||
variables: commonVariables,
|
||||
variables: { marketId },
|
||||
});
|
||||
let totalMargin = '0';
|
||||
let margin = '0';
|
||||
|
@ -1,2 +1,4 @@
|
||||
export * from './market-info';
|
||||
export * from './last-24h-price-change';
|
||||
export * from './last-24h-volume';
|
||||
export * from './fees-breakdown';
|
||||
|
@ -9,7 +9,6 @@ import { PriceChangeCell } from '@vegaprotocol/datagrid';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
@ -28,8 +27,7 @@ export const Last24hPriceChange = ({
|
||||
}: Props) => {
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const yesterday = useYesterday();
|
||||
const { data, error } = useThrottledDataProvider(
|
||||
{
|
||||
const { data, error } = useThrottledDataProvider({
|
||||
dataProvider: marketCandlesProvider,
|
||||
variables: {
|
||||
marketId: marketId || '',
|
||||
@ -37,9 +35,7 @@ export const Last24hPriceChange = ({
|
||||
since: new Date(yesterday).toISOString(),
|
||||
},
|
||||
skip: !marketId || !inView,
|
||||
},
|
||||
THROTTLE_UPDATE_TIME
|
||||
);
|
||||
});
|
||||
|
||||
const candles =
|
||||
data
|
@ -10,7 +10,6 @@ import {
|
||||
useYesterday,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { THROTTLE_UPDATE_TIME } from '../constants';
|
||||
|
||||
interface Props {
|
||||
marketId?: string;
|
||||
@ -30,8 +29,7 @@ export const Last24hVolume = ({
|
||||
const yesterday = useYesterday();
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
|
||||
const { data } = useThrottledDataProvider(
|
||||
{
|
||||
const { data } = useThrottledDataProvider({
|
||||
dataProvider: marketCandlesProvider,
|
||||
variables: {
|
||||
marketId: marketId || '',
|
||||
@ -39,9 +37,7 @@ export const Last24hVolume = ({
|
||||
since: new Date(yesterday).toISOString(),
|
||||
},
|
||||
skip: !(inView && marketId),
|
||||
},
|
||||
THROTTLE_UPDATE_TIME
|
||||
);
|
||||
});
|
||||
const candleVolume = data ? calcCandleVolume(data) : initialValue;
|
||||
return (
|
||||
<span ref={ref}>
|
@ -1,5 +1,5 @@
|
||||
export * from './info-key-value-table';
|
||||
export * from './info-market';
|
||||
export * from './market-info-accordion';
|
||||
export * from './tooltip-mapping';
|
||||
export * from './__generated__/MarketInfo';
|
||||
export * from './market-info-data-provider';
|
||||
|
@ -17,7 +17,7 @@ import { tooltipMapping } from './tooltip-mapping';
|
||||
import type { ReactNode } from 'react';
|
||||
interface RowProps {
|
||||
field: string;
|
||||
value: unknown;
|
||||
value: ReactNode;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
@ -36,8 +36,8 @@ const Row = ({
|
||||
}: RowProps) => {
|
||||
const className = 'text-black dark:text-white text-sm !px-0';
|
||||
|
||||
const getFormattedValue = (value: unknown) => {
|
||||
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
||||
const getFormattedValue = (value: ReactNode) => {
|
||||
if (typeof value !== 'string' && typeof value !== 'number') return value;
|
||||
if (unformatted || isNaN(Number(value))) {
|
||||
return value;
|
||||
}
|
||||
@ -50,7 +50,7 @@ const Row = ({
|
||||
return `${formatNumber(Number(value))} ${assetSymbol}`;
|
||||
};
|
||||
|
||||
const formattedValue: string | number | null = getFormattedValue(value);
|
||||
const formattedValue = getFormattedValue(value);
|
||||
|
||||
if (!formattedValue) return null;
|
||||
return (
|
||||
@ -70,11 +70,10 @@ const Row = ({
|
||||
};
|
||||
|
||||
export interface MarketInfoTableProps {
|
||||
data: unknown;
|
||||
data: Record<string, ReactNode> | null | undefined;
|
||||
decimalPlaces?: number;
|
||||
asPercentage?: boolean;
|
||||
unformatted?: boolean;
|
||||
omits?: string[];
|
||||
children?: ReactNode;
|
||||
assetSymbol?: string;
|
||||
noBorder?: boolean;
|
||||
@ -85,7 +84,6 @@ export const MarketInfoTable = ({
|
||||
decimalPlaces,
|
||||
asPercentage,
|
||||
unformatted,
|
||||
omits = ['__typename'],
|
||||
children,
|
||||
assetSymbol,
|
||||
noBorder,
|
||||
@ -96,9 +94,7 @@ export const MarketInfoTable = ({
|
||||
return (
|
||||
<>
|
||||
<KeyValueTable>
|
||||
{Object.entries(data)
|
||||
.filter(([key]) => !omits.includes(key))
|
||||
.map(([key, value]) => (
|
||||
{Object.entries(data).map(([key, value]) => (
|
||||
<Row
|
||||
key={key}
|
||||
field={key}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper, TokenLinks } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider, useYesterday } from '@vegaprotocol/react-helpers';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
Accordion,
|
||||
@ -11,12 +11,11 @@ import {
|
||||
Splash,
|
||||
TinyScroll,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useMemo } from 'react';
|
||||
import { generatePath, Link } from 'react-router-dom';
|
||||
|
||||
import { marketInfoWithDataAndCandlesProvider } from './market-info-data-provider';
|
||||
import { marketInfoProvider } from './market-info-data-provider';
|
||||
|
||||
import type { MarketInfoWithDataAndCandles } from './market-info-data-provider';
|
||||
import type { MarketInfo } from './market-info-data-provider';
|
||||
import { MarketProposalNotification } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
CurrentFeesInfoPanel,
|
||||
@ -37,8 +36,8 @@ import {
|
||||
SettlementAssetInfoPanel,
|
||||
} from './market-info-panels';
|
||||
|
||||
export interface InfoProps {
|
||||
market: MarketInfoWithDataAndCandles;
|
||||
export interface MarketInfoAccordionProps {
|
||||
market: MarketInfo;
|
||||
onSelect?: (id: string, metaKey?: boolean) => void;
|
||||
}
|
||||
|
||||
@ -46,34 +45,21 @@ export interface MarketInfoContainerProps {
|
||||
marketId: string;
|
||||
onSelect?: (id: string, metaKey?: boolean) => void;
|
||||
}
|
||||
export const MarketInfoContainer = ({
|
||||
export const MarketInfoAccordionContainer = ({
|
||||
marketId,
|
||||
onSelect,
|
||||
}: MarketInfoContainerProps) => {
|
||||
const yesterday = useYesterday();
|
||||
const yTimestamp = useMemo(() => {
|
||||
return new Date(yesterday).toISOString();
|
||||
}, [yesterday]);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId,
|
||||
since: yTimestamp,
|
||||
interval: Schema.Interval.INTERVAL_I1H,
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
);
|
||||
|
||||
const { data, loading, error, reload } = useDataProvider({
|
||||
dataProvider: marketInfoWithDataAndCandlesProvider,
|
||||
dataProvider: marketInfoProvider,
|
||||
skipUpdates: true,
|
||||
variables,
|
||||
variables: { marketId },
|
||||
});
|
||||
|
||||
return (
|
||||
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
|
||||
{data ? (
|
||||
<TinyScroll className="h-full overflow-auto">
|
||||
<Info market={data} onSelect={onSelect} />
|
||||
<MarketInfoAccordion market={data} onSelect={onSelect} />
|
||||
</TinyScroll>
|
||||
) : (
|
||||
<Splash>
|
||||
@ -84,7 +70,10 @@ export const MarketInfoContainer = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
const MarketInfoAccordion = ({
|
||||
market,
|
||||
onSelect,
|
||||
}: MarketInfoAccordionProps) => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const headerClassName = 'uppercase text-lg';
|
||||
|
@ -3,15 +3,8 @@ import type {
|
||||
MarketInfoQuery,
|
||||
MarketInfoQueryVariables,
|
||||
} from './__generated__/MarketInfo';
|
||||
import {
|
||||
marketDataProvider,
|
||||
marketCandlesProvider,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import type {
|
||||
MarketData,
|
||||
Candle,
|
||||
MarketCandlesQueryVariables,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import type { MarketData, Candle } from '@vegaprotocol/market-list';
|
||||
import { MarketInfoDocument } from './__generated__/MarketInfo';
|
||||
|
||||
export type MarketInfo = NonNullable<MarketInfoQuery['market']>;
|
||||
@ -49,20 +42,3 @@ export const marketInfoWithDataProvider = makeDerivedDataProvider<
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export const marketInfoWithDataAndCandlesProvider = makeDerivedDataProvider<
|
||||
MarketInfoWithDataAndCandles,
|
||||
never,
|
||||
MarketCandlesQueryVariables
|
||||
>([marketInfoProvider, marketDataProvider, marketCandlesProvider], (parts) => {
|
||||
const market: MarketInfo | null = parts[0];
|
||||
const marketData: MarketData | null = parts[1];
|
||||
const candles: Candle[] | null = parts[2];
|
||||
return (
|
||||
market && {
|
||||
...market,
|
||||
data: marketData || undefined,
|
||||
candles: candles || undefined,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -3,8 +3,8 @@ import { useMemo } from 'react';
|
||||
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
calcCandleVolume,
|
||||
totalFeesPercentage,
|
||||
marketDataProvider,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
@ -18,8 +18,8 @@ import { MarketInfoTable } from './info-key-value-table';
|
||||
import type {
|
||||
MarketInfo,
|
||||
MarketInfoWithData,
|
||||
MarketInfoWithDataAndCandles,
|
||||
} from './market-info-data-provider';
|
||||
import { Last24hVolume } from '../last-24h-volume';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { DataSourceDefinition, SignerKind } from '@vegaprotocol/types';
|
||||
import { ConditionOperatorMapping } from '@vegaprotocol/types';
|
||||
@ -27,6 +27,7 @@ import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { Provider } from '@vegaprotocol/oracles';
|
||||
import { useOracleProofs } from '@vegaprotocol/oracles';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
|
||||
type PanelProps = Pick<
|
||||
ComponentProps<typeof MarketInfoTable>,
|
||||
@ -37,14 +38,6 @@ type MarketInfoProps = {
|
||||
market: MarketInfo;
|
||||
};
|
||||
|
||||
type MarketInfoWithDataProps = {
|
||||
market: MarketInfoWithData;
|
||||
};
|
||||
|
||||
type MarketInfoWithDataAndCandlesProps = {
|
||||
market: MarketInfoWithDataAndCandles;
|
||||
};
|
||||
|
||||
export const CurrentFeesInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
@ -52,7 +45,9 @@ export const CurrentFeesInfoPanel = ({
|
||||
<>
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
...market.fees.factors,
|
||||
makerFee: market.fees.factors.makerFee,
|
||||
infrastructureFee: market.fees.factors.infrastructureFee,
|
||||
liquidityFee: market.fees.factors.liquidityFee,
|
||||
totalFees: totalFeesPercentage(market.fees.factors),
|
||||
}}
|
||||
asPercentage={true}
|
||||
@ -69,18 +64,22 @@ export const CurrentFeesInfoPanel = ({
|
||||
export const MarketPriceInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoWithDataProps & PanelProps) => {
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
const assetSymbol =
|
||||
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
|
||||
const quoteUnit =
|
||||
market?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
markPrice: market.data?.markPrice,
|
||||
bestBidPrice: market.data?.bestBidPrice,
|
||||
bestOfferPrice: market.data?.bestOfferPrice,
|
||||
markPrice: data?.markPrice,
|
||||
bestBidPrice: data?.bestBidPrice,
|
||||
bestOfferPrice: data?.bestOfferPrice,
|
||||
quoteUnit: market.tradableInstrument.instrument.product.quoteName,
|
||||
}}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
@ -99,8 +98,11 @@ export const MarketPriceInfoPanel = ({
|
||||
export const MarketVolumeInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoWithDataAndCandlesProps & PanelProps) => {
|
||||
const last24hourVolume = market.candles && calcCandleVolume(market.candles);
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
|
||||
const dash = (value: string | undefined) =>
|
||||
value && value !== '0' ? value : '-';
|
||||
@ -108,12 +110,17 @@ export const MarketVolumeInfoPanel = ({
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
'24hourVolume': dash(last24hourVolume),
|
||||
openInterest: dash(market.data?.openInterest),
|
||||
bestBidVolume: dash(market.data?.bestBidVolume),
|
||||
bestOfferVolume: dash(market.data?.bestOfferVolume),
|
||||
bestStaticBidVolume: dash(market.data?.bestStaticBidVolume),
|
||||
bestStaticOfferVolume: dash(market.data?.bestStaticOfferVolume),
|
||||
'24hourVolume': (
|
||||
<Last24hVolume
|
||||
marketId={market.id}
|
||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
||||
/>
|
||||
),
|
||||
openInterest: dash(data?.openInterest),
|
||||
bestBidVolume: dash(data?.bestBidVolume),
|
||||
bestOfferVolume: dash(data?.bestOfferVolume),
|
||||
bestStaticBidVolume: dash(data?.bestStaticBidVolume),
|
||||
bestStaticOfferVolume: dash(data?.bestStaticOfferVolume),
|
||||
}}
|
||||
decimalPlaces={market.positionDecimalPlaces}
|
||||
{...props}
|
||||
@ -176,7 +183,7 @@ export const InstrumentInfoPanel = ({
|
||||
marketName: market.tradableInstrument.instrument.name,
|
||||
code: market.tradableInstrument.instrument.code,
|
||||
productType: market.tradableInstrument.instrument.product.__typename,
|
||||
...market.tradableInstrument.instrument.product,
|
||||
quoteName: market.tradableInstrument.instrument.product.quoteName,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
@ -239,38 +246,52 @@ export const MetadataInfoPanel = ({
|
||||
export const RiskModelInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoProps & PanelProps) => (
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
if (market.tradableInstrument.riskModel.__typename !== 'LogNormalRiskModel') {
|
||||
return null;
|
||||
}
|
||||
const { tau, riskAversionParameter } = market.tradableInstrument.riskModel;
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={market.tradableInstrument.riskModel}
|
||||
unformatted={true}
|
||||
omits={[]}
|
||||
data={{ tau, riskAversionParameter }}
|
||||
unformatted
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const RiskParametersInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoProps & PanelProps) => (
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
if (market.tradableInstrument.riskModel.__typename === 'LogNormalRiskModel') {
|
||||
const { r, sigma, mu } = market.tradableInstrument.riskModel.params;
|
||||
return <MarketInfoTable data={{ r, sigma, mu }} unformatted {...props} />;
|
||||
}
|
||||
if (market.tradableInstrument.riskModel.__typename === 'SimpleRiskModel') {
|
||||
const { factorLong, factorShort } =
|
||||
market.tradableInstrument.riskModel.params;
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={market.tradableInstrument.riskModel.params}
|
||||
unformatted={true}
|
||||
omits={[]}
|
||||
data={{ factorLong, factorShort }}
|
||||
unformatted
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const RiskFactorsInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoProps & PanelProps) => (
|
||||
<MarketInfoTable
|
||||
data={market.riskFactors}
|
||||
unformatted={true}
|
||||
omits={['market', '__typename']}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
if (!market.riskFactors) {
|
||||
return null;
|
||||
}
|
||||
const { short, long } = market.riskFactors;
|
||||
return <MarketInfoTable data={{ short, long }} unformatted {...props} />;
|
||||
};
|
||||
|
||||
export const PriceMonitoringBoundsInfoPanel = ({
|
||||
market,
|
||||
@ -278,13 +299,17 @@ export const PriceMonitoringBoundsInfoPanel = ({
|
||||
...props
|
||||
}: {
|
||||
triggerIndex: number;
|
||||
} & MarketInfoWithDataProps &
|
||||
} & MarketInfoProps &
|
||||
PanelProps) => {
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
const quoteUnit =
|
||||
market?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const trigger =
|
||||
market.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
|
||||
const bounds = market.data?.priceMonitoringBounds?.[triggerIndex];
|
||||
const bounds = data?.priceMonitoringBounds?.[triggerIndex];
|
||||
if (!trigger) {
|
||||
console.error(
|
||||
`Could not find data for trigger ${triggerIndex} (market id: ${market.id})`
|
||||
@ -334,7 +359,11 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
|
||||
...market.liquidityMonitoringParameters.targetStakeParameters,
|
||||
timeWindow:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
|
||||
scalingFactor:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters
|
||||
.scalingFactor,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
@ -343,17 +372,21 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
export const LiquidityInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoWithDataProps & PanelProps) => {
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
const assetDecimals =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.decimals;
|
||||
const assetSymbol =
|
||||
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
return (
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
targetStake: market.data && market.data.targetStake,
|
||||
suppliedStake: market.data && market.data?.suppliedStake,
|
||||
marketValueProxy: market.data && market.data.marketValueProxy,
|
||||
targetStake: data?.targetStake,
|
||||
suppliedStake: data?.suppliedStake,
|
||||
marketValueProxy: data?.marketValueProxy,
|
||||
}}
|
||||
decimalPlaces={assetDecimals}
|
||||
assetSymbol={assetSymbol}
|
||||
@ -365,12 +398,16 @@ export const LiquidityInfoPanel = ({
|
||||
export const LiquidityPriceRangeInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoWithDataProps & PanelProps) => {
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
const quoteUnit =
|
||||
market?.tradableInstrument.instrument.product?.quoteName || '';
|
||||
const liquidityPriceRange = formatNumberPercentage(
|
||||
new BigNumber(market.lpPriceRange).times(100)
|
||||
);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId: market.id },
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<p className="text-xs mb-4">
|
||||
@ -386,20 +423,20 @@ export const LiquidityPriceRangeInfoPanel = ({
|
||||
data={{
|
||||
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
|
||||
lowestPrice:
|
||||
market.data?.midPrice &&
|
||||
data?.midPrice &&
|
||||
`${addDecimalsFormatNumber(
|
||||
new BigNumber(1)
|
||||
.minus(market.lpPriceRange)
|
||||
.times(market.data.midPrice)
|
||||
.times(data.midPrice)
|
||||
.toString(),
|
||||
market.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
highestPrice:
|
||||
market.data?.midPrice &&
|
||||
data?.midPrice &&
|
||||
`${addDecimalsFormatNumber(
|
||||
new BigNumber(1)
|
||||
.plus(market.lpPriceRange)
|
||||
.times(market.data.midPrice)
|
||||
.times(data.midPrice)
|
||||
.toString(),
|
||||
market.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
@ -418,7 +455,15 @@ export const OracleInfoPanel = ({
|
||||
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
|
||||
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||
return (
|
||||
<MarketInfoTable data={product.dataSourceSpecBinding} {...props}>
|
||||
<MarketInfoTable
|
||||
data={{
|
||||
settlementDataProperty:
|
||||
product.dataSourceSpecBinding.settlementDataProperty,
|
||||
tradingTerminationProperty:
|
||||
product.dataSourceSpecBinding.tradingTerminationProperty,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className="flex flex-col gap-2 mt-4"
|
||||
data-testid="oracle-proof-links"
|
||||
|
@ -354,10 +354,10 @@ export const volumeAndMarginProvider = makeDerivedDataProvider<
|
||||
partyId,
|
||||
marketId,
|
||||
}),
|
||||
(callback, client, variables) =>
|
||||
marketDataProvider(callback, client, { marketId: variables.marketId }),
|
||||
(callback, client, variables) =>
|
||||
marketInfoProvider(callback, client, { marketId: variables.marketId }),
|
||||
(callback, client, { marketId }) =>
|
||||
marketDataProvider(callback, client, { marketId }),
|
||||
(callback, client, { marketId }) =>
|
||||
marketInfoProvider(callback, client, { marketId }),
|
||||
openVolumeDataProvider,
|
||||
],
|
||||
(data) => {
|
||||
|
@ -6,7 +6,7 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
type Data = number;
|
||||
type Delta = number;
|
||||
type Variables = { partyId: string };
|
||||
type Variables = { partyId: string; marketIds?: string[] };
|
||||
|
||||
const unsubscribe = jest.fn();
|
||||
const reload = jest.fn();
|
||||
@ -156,6 +156,121 @@ describe('useDataProvider hook', () => {
|
||||
expect(insert.mock.calls[1][0].insertionData).toEqual(insertionData);
|
||||
});
|
||||
|
||||
it('calls dataProvider with updated variables if skip switched to true', async () => {
|
||||
const { rerender } = render({
|
||||
dataProvider,
|
||||
variables: { partyId: '' },
|
||||
skip: true,
|
||||
});
|
||||
expect(dataProvider).toBeCalledTimes(0);
|
||||
rerender({
|
||||
dataProvider,
|
||||
variables,
|
||||
});
|
||||
expect(dataProvider).toBeCalledTimes(1);
|
||||
expect(dataProvider.mock.calls[0][2]).toEqual(variables);
|
||||
});
|
||||
it('uses same data provider when rerendered with equal variables', async () => {
|
||||
const { rerender } = render({
|
||||
dataProvider,
|
||||
variables: { ...variables, marketIds: ['a', 'b'] },
|
||||
});
|
||||
expect(dataProvider).toBeCalledTimes(1);
|
||||
rerender({
|
||||
dataProvider,
|
||||
variables: { ...variables, marketIds: ['b', 'a'] },
|
||||
});
|
||||
expect(dataProvider).toBeCalledTimes(1);
|
||||
rerender({
|
||||
dataProvider,
|
||||
variables: { ...variables },
|
||||
});
|
||||
expect(dataProvider).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it('calls new update and insert when replaced', async () => {
|
||||
const { rerender } = render({
|
||||
dataProvider,
|
||||
update,
|
||||
insert,
|
||||
variables,
|
||||
});
|
||||
const data = 0;
|
||||
const delta = 0;
|
||||
const insertionData = 0;
|
||||
const callback = dataProvider.mock.calls[0][0];
|
||||
await act(async () => {
|
||||
callback({ ...updateCallbackPayload, data });
|
||||
});
|
||||
const newUpdate = jest.fn();
|
||||
const newInsert = jest.fn();
|
||||
expect(update).toBeCalledTimes(2);
|
||||
expect(insert).toBeCalledTimes(0);
|
||||
rerender({ dataProvider, update: newUpdate, insert: newInsert, variables });
|
||||
await act(async () => {
|
||||
callback({
|
||||
...updateCallbackPayload,
|
||||
data: data,
|
||||
delta,
|
||||
isUpdate: true,
|
||||
});
|
||||
});
|
||||
expect(newUpdate).toBeCalledTimes(1);
|
||||
await act(async () => {
|
||||
callback({
|
||||
...updateCallbackPayload,
|
||||
data,
|
||||
insertionData,
|
||||
isInsert: true,
|
||||
});
|
||||
});
|
||||
expect(newUpdate).toBeCalledTimes(2);
|
||||
expect(newInsert).toBeCalledTimes(1);
|
||||
expect(update).toBeCalledTimes(2);
|
||||
expect(insert).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it('skip updates if skipUpdates is true', async () => {
|
||||
const { result, rerender } = render({
|
||||
dataProvider,
|
||||
update,
|
||||
variables,
|
||||
skipUpdates: true,
|
||||
});
|
||||
expect(update).toBeCalledTimes(1);
|
||||
let data = 0;
|
||||
const delta = 1;
|
||||
const callback = dataProvider.mock.calls[0][0];
|
||||
await act(async () => {
|
||||
callback({ ...updateCallbackPayload, data });
|
||||
});
|
||||
expect(update).toBeCalledTimes(2);
|
||||
expect(result.current.data).toEqual(data);
|
||||
await act(async () => {
|
||||
callback({
|
||||
...updateCallbackPayload,
|
||||
data: data + delta,
|
||||
delta,
|
||||
isUpdate: true,
|
||||
});
|
||||
});
|
||||
expect(update).toBeCalledTimes(2);
|
||||
expect(result.current.data).toEqual(data);
|
||||
rerender({ dataProvider, variables, update });
|
||||
expect(update).toBeCalledTimes(2);
|
||||
await act(async () => {
|
||||
callback({
|
||||
...updateCallbackPayload,
|
||||
data: (data = data + delta),
|
||||
delta,
|
||||
isUpdate: true,
|
||||
});
|
||||
});
|
||||
expect(update).toBeCalledTimes(3);
|
||||
expect(result.current.data).toEqual(data);
|
||||
expect(dataProvider).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('change data provider instance on variables change', async () => {
|
||||
const { result, rerender } = render({ dataProvider, update, variables });
|
||||
const callback = dataProvider.mock.calls[0][0];
|
||||
|
@ -63,6 +63,9 @@ export const useDataProvider = <
|
||||
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
||||
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
||||
const variablesRef = useRef<Variables>(props.variables);
|
||||
const updateRef = useRef(update);
|
||||
const insertRef = useRef(insert);
|
||||
const skipUpdatesRef = useRef(skipUpdates);
|
||||
const variables = useMemo(() => {
|
||||
if (
|
||||
!isEqualWith(
|
||||
@ -91,8 +94,7 @@ export const useDataProvider = <
|
||||
}
|
||||
return Promise.reject();
|
||||
}, []);
|
||||
const callback = useCallback<UpdateCallback<Data, Delta>>(
|
||||
(args) => {
|
||||
const callback = useCallback<UpdateCallback<Data, Delta>>((args) => {
|
||||
const {
|
||||
data,
|
||||
delta,
|
||||
@ -110,35 +112,51 @@ export const useDataProvider = <
|
||||
if (!loading) {
|
||||
if (
|
||||
isUpdate &&
|
||||
!skipUpdates &&
|
||||
update &&
|
||||
update({ delta, data, totalCount })
|
||||
(skipUpdatesRef.current ||
|
||||
(!skipUpdatesRef.current &&
|
||||
updateRef.current &&
|
||||
updateRef.current({ delta, data, totalCount })))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isInsert && insert && insert({ insertionData, data, totalCount })) {
|
||||
if (
|
||||
isInsert &&
|
||||
insertRef.current &&
|
||||
insertRef.current({ insertionData, data, totalCount })
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
setTotalCount(totalCount);
|
||||
setData(data);
|
||||
if (!loading && !isUpdate && update) {
|
||||
update({ data });
|
||||
if (!loading && !isUpdate && updateRef.current) {
|
||||
updateRef.current({ data });
|
||||
}
|
||||
},
|
||||
[update, insert, skipUpdates]
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
updateRef.current = update;
|
||||
}, [update]);
|
||||
|
||||
useEffect(() => {
|
||||
insertRef.current = insert;
|
||||
}, [insert]);
|
||||
|
||||
useEffect(() => {
|
||||
skipUpdatesRef.current = skipUpdates;
|
||||
}, [skipUpdates]);
|
||||
|
||||
useEffect(() => {
|
||||
setData(null);
|
||||
setError(undefined);
|
||||
setTotalCount(undefined);
|
||||
if (update) {
|
||||
update({ data: null });
|
||||
if (updateRef.current) {
|
||||
updateRef.current({ data: null });
|
||||
}
|
||||
if (skip) {
|
||||
setLoading(false);
|
||||
if (update) {
|
||||
update({ data: null });
|
||||
if (updateRef.current) {
|
||||
updateRef.current({ data: null });
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -157,7 +175,7 @@ export const useDataProvider = <
|
||||
loadRef.current = undefined;
|
||||
return unsubscribe();
|
||||
};
|
||||
}, [client, dataProvider, callback, variables, skip, update]);
|
||||
}, [client, dataProvider, callback, variables, skip]);
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
@ -175,7 +193,7 @@ export const useThrottledDataProvider = <
|
||||
Variables extends OperationVariables = OperationVariables
|
||||
>(
|
||||
params: Omit<useDataProviderParams<Data, Delta, Variables>, 'update'>,
|
||||
wait?: number
|
||||
wait = 500
|
||||
) => {
|
||||
const [data, setData] = useState<Data | null>(null);
|
||||
const dataRef = useRef<Data | null>(null);
|
||||
|
Loading…
Reference in New Issue
Block a user