diff --git a/.github/workflows/ci-cd-trigger.yml b/.github/workflows/ci-cd-trigger.yml index 381e7696f..4e8a88113 100644 --- a/.github/workflows/ci-cd-trigger.yml +++ b/.github/workflows/ci-cd-trigger.yml @@ -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' diff --git a/apps/explorer/src/app/components/markets/market-details.tsx b/apps/explorer/src/app/components/markets/market-details.tsx index 4395782e6..218371b63 100644 --- a/apps/explorer/src/app/components/markets/market-details.tsx +++ b/apps/explorer/src/app/components/markets/market-details.tsx @@ -60,9 +60,11 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => { <> { 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', () => { diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index 7fc750c27..6ba4a0ca9 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -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, diff --git a/apps/trading/client-pages/market/trade-market-header.tsx b/apps/trading/client-pages/market/trade-market-header.tsx index 96ebae4f3..94c44ea9d 100644 --- a/apps/trading/client-pages/market/trade-market-header.tsx +++ b/apps/trading/client-pages/market/trade-market-header.tsx @@ -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'; diff --git a/apps/trading/components/select-market/select-market-columns.tsx b/apps/trading/components/select-market/select-market-columns.tsx index fab44b1b2..73df026b7 100644 --- a/apps/trading/components/select-market/select-market-columns.tsx +++ b/apps/trading/components/select-market/select-market-columns.tsx @@ -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'; diff --git a/libs/deal-ticket/src/hooks/use-initial-margin.ts b/libs/deal-ticket/src/hooks/use-initial-margin.ts index 1db1d2e25..b431123a2 100644 --- a/libs/deal-ticket/src/hooks/use-initial-margin.ts +++ b/libs/deal-ticket/src/hooks/use-initial-margin.ts @@ -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'; diff --git a/libs/market-info/src/components/index.ts b/libs/market-info/src/components/index.ts index fb6109f53..a1757c56e 100644 --- a/libs/market-info/src/components/index.ts +++ b/libs/market-info/src/components/index.ts @@ -1,2 +1,4 @@ export * from './market-info'; +export * from './last-24h-price-change'; +export * from './last-24h-volume'; export * from './fees-breakdown'; diff --git a/apps/trading/components/last-24h-price-change/index.ts b/libs/market-info/src/components/last-24h-price-change/index.ts similarity index 100% rename from apps/trading/components/last-24h-price-change/index.ts rename to libs/market-info/src/components/last-24h-price-change/index.ts diff --git a/apps/trading/components/last-24h-price-change/last-24h-price-change.tsx b/libs/market-info/src/components/last-24h-price-change/last-24h-price-change.tsx similarity index 76% rename from apps/trading/components/last-24h-price-change/last-24h-price-change.tsx rename to libs/market-info/src/components/last-24h-price-change/last-24h-price-change.tsx index 3e6942032..aae0e87e2 100644 --- a/apps/trading/components/last-24h-price-change/last-24h-price-change.tsx +++ b/libs/market-info/src/components/last-24h-price-change/last-24h-price-change.tsx @@ -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,18 +27,15 @@ export const Last24hPriceChange = ({ }: Props) => { const [ref, inView] = useInView({ root: inViewRoot?.current }); const yesterday = useYesterday(); - const { data, error } = useThrottledDataProvider( - { - dataProvider: marketCandlesProvider, - variables: { - marketId: marketId || '', - interval: Schema.Interval.INTERVAL_I1H, - since: new Date(yesterday).toISOString(), - }, - skip: !marketId || !inView, + const { data, error } = useThrottledDataProvider({ + dataProvider: marketCandlesProvider, + variables: { + marketId: marketId || '', + interval: Schema.Interval.INTERVAL_I1H, + since: new Date(yesterday).toISOString(), }, - THROTTLE_UPDATE_TIME - ); + skip: !marketId || !inView, + }); const candles = data diff --git a/apps/trading/components/last-24h-volume/index.ts b/libs/market-info/src/components/last-24h-volume/index.ts similarity index 100% rename from apps/trading/components/last-24h-volume/index.ts rename to libs/market-info/src/components/last-24h-volume/index.ts diff --git a/apps/trading/components/last-24h-volume/last-24h-volume.tsx b/libs/market-info/src/components/last-24h-volume/last-24h-volume.tsx similarity index 75% rename from apps/trading/components/last-24h-volume/last-24h-volume.tsx rename to libs/market-info/src/components/last-24h-volume/last-24h-volume.tsx index e5c59e0bd..b0b759366 100644 --- a/apps/trading/components/last-24h-volume/last-24h-volume.tsx +++ b/libs/market-info/src/components/last-24h-volume/last-24h-volume.tsx @@ -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,18 +29,15 @@ export const Last24hVolume = ({ const yesterday = useYesterday(); const [ref, inView] = useInView({ root: inViewRoot?.current }); - const { data } = useThrottledDataProvider( - { - dataProvider: marketCandlesProvider, - variables: { - marketId: marketId || '', - interval: Schema.Interval.INTERVAL_I1H, - since: new Date(yesterday).toISOString(), - }, - skip: !(inView && marketId), + const { data } = useThrottledDataProvider({ + dataProvider: marketCandlesProvider, + variables: { + marketId: marketId || '', + interval: Schema.Interval.INTERVAL_I1H, + since: new Date(yesterday).toISOString(), }, - THROTTLE_UPDATE_TIME - ); + skip: !(inView && marketId), + }); const candleVolume = data ? calcCandleVolume(data) : initialValue; return ( diff --git a/libs/market-info/src/components/market-info/index.ts b/libs/market-info/src/components/market-info/index.ts index 828b9060c..b82cf2ba3 100644 --- a/libs/market-info/src/components/market-info/index.ts +++ b/libs/market-info/src/components/market-info/index.ts @@ -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'; diff --git a/libs/market-info/src/components/market-info/info-key-value-table.tsx b/libs/market-info/src/components/market-info/info-key-value-table.tsx index f77ff38a6..e2c38818f 100644 --- a/libs/market-info/src/components/market-info/info-key-value-table.tsx +++ b/libs/market-info/src/components/market-info/info-key-value-table.tsx @@ -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 | 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,20 +94,18 @@ export const MarketInfoTable = ({ return ( <> - {Object.entries(data) - .filter(([key]) => !omits.includes(key)) - .map(([key, value]) => ( - - ))} + {Object.entries(data).map(([key, value]) => ( + + ))}
{children}
diff --git a/libs/market-info/src/components/market-info/info-market.tsx b/libs/market-info/src/components/market-info/market-info-accordion.tsx similarity index 87% rename from libs/market-info/src/components/market-info/info-market.tsx rename to libs/market-info/src/components/market-info/market-info-accordion.tsx index 99a2bdb9d..c6dc747cb 100644 --- a/libs/market-info/src/components/market-info/info-market.tsx +++ b/libs/market-info/src/components/market-info/market-info-accordion.tsx @@ -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 ( {data ? ( - + ) : ( @@ -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'; diff --git a/libs/market-info/src/components/market-info/market-info-data-provider.ts b/libs/market-info/src/components/market-info/market-info-data-provider.ts index 7e6905d15..366953241 100644 --- a/libs/market-info/src/components/market-info/market-info-data-provider.ts +++ b/libs/market-info/src/components/market-info/market-info-data-provider.ts @@ -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; @@ -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, - } - ); -}); diff --git a/libs/market-info/src/components/market-info/market-info-panels.tsx b/libs/market-info/src/components/market-info/market-info-panels.tsx index cb635ef0f..c965935f2 100644 --- a/libs/market-info/src/components/market-info/market-info-panels.tsx +++ b/libs/market-info/src/components/market-info/market-info-panels.tsx @@ -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, @@ -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 = ({ <> { +}: 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 ( <> { - 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 ( + ), + 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 ( + + ); +}; export const RiskParametersInfoPanel = ({ market, ...props -}: MarketInfoProps & PanelProps) => ( - -); +}: MarketInfoProps & PanelProps) => { + if (market.tradableInstrument.riskModel.__typename === 'LogNormalRiskModel') { + const { r, sigma, mu } = market.tradableInstrument.riskModel.params; + return ; + } + if (market.tradableInstrument.riskModel.__typename === 'SimpleRiskModel') { + const { factorLong, factorShort } = + market.tradableInstrument.riskModel.params; + return ( + + ); + } + return null; +}; export const RiskFactorsInfoPanel = ({ market, ...props -}: MarketInfoProps & PanelProps) => ( - -); +}: MarketInfoProps & PanelProps) => { + if (!market.riskFactors) { + return null; + } + const { short, long } = market.riskFactors; + return ; +}; 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 = ({ @@ -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 ( { +}: 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 ( <>

@@ -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 ( - +

- 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) => { diff --git a/libs/react-helpers/src/hooks/use-data-provider.spec.ts b/libs/react-helpers/src/hooks/use-data-provider.spec.ts index 78fdb4b00..a6f8e3c0b 100644 --- a/libs/react-helpers/src/hooks/use-data-provider.spec.ts +++ b/libs/react-helpers/src/hooks/use-data-provider.spec.ts @@ -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]; diff --git a/libs/react-helpers/src/hooks/use-data-provider.ts b/libs/react-helpers/src/hooks/use-data-provider.ts index 3419983fc..372b250e1 100644 --- a/libs/react-helpers/src/hooks/use-data-provider.ts +++ b/libs/react-helpers/src/hooks/use-data-provider.ts @@ -63,6 +63,9 @@ export const useDataProvider = < const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined); const loadRef = useRef | undefined>(undefined); const variablesRef = useRef(props.variables); + const updateRef = useRef(update); + const insertRef = useRef(insert); + const skipUpdatesRef = useRef(skipUpdates); const variables = useMemo(() => { if ( !isEqualWith( @@ -91,54 +94,69 @@ export const useDataProvider = < } return Promise.reject(); }, []); - const callback = useCallback>( - (args) => { - const { - data, - delta, - error, - loading, - insertionData, - totalCount, - isInsert, - isUpdate, - } = args; - setError(error); - setLoading(loading); - // if update or insert function returns true it means that component handles updates - // component can use flush() which will call callback without delta and cause data state update - if (!loading) { - if ( - isUpdate && - !skipUpdates && - update && - update({ delta, data, totalCount }) - ) { - return; - } - if (isInsert && insert && insert({ insertionData, data, totalCount })) { - return; - } + const callback = useCallback>((args) => { + const { + data, + delta, + error, + loading, + insertionData, + totalCount, + isInsert, + isUpdate, + } = args; + setError(error); + setLoading(loading); + // if update or insert function returns true it means that component handles updates + // component can use flush() which will call callback without delta and cause data state update + if (!loading) { + if ( + isUpdate && + (skipUpdatesRef.current || + (!skipUpdatesRef.current && + updateRef.current && + updateRef.current({ delta, data, totalCount }))) + ) { + return; } - setTotalCount(totalCount); - setData(data); - if (!loading && !isUpdate && update) { - update({ data }); + if ( + isInsert && + insertRef.current && + insertRef.current({ insertionData, data, totalCount }) + ) { + return; } - }, - [update, insert, skipUpdates] - ); + } + setTotalCount(totalCount); + setData(data); + if (!loading && !isUpdate && updateRef.current) { + updateRef.current({ data }); + } + }, []); + + 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, 'update'>, - wait?: number + wait = 500 ) => { const [data, setData] = useState(null); const dataRef = useRef(null);