Add pagination support to generic-data-provider (#691)
* feat(#638): add pagination support to data-provider * feat(#638): use infinite rowModelType in market list table * chore(#638): code style fixes * feat(#638): fix data provider post update callbacks, handle market list table empty data * feat(#638): amend variable names to improve code readability
This commit is contained in:
parent
db050c6560
commit
b9aef78447
@ -91,14 +91,15 @@ export const FILTERS_QUERY = gql`
|
|||||||
const update = (
|
const update = (
|
||||||
data: SimpleMarkets_markets[],
|
data: SimpleMarkets_markets[],
|
||||||
delta: SimpleMarketDataSub_marketData
|
delta: SimpleMarketDataSub_marketData
|
||||||
) =>
|
) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const index = draft.findIndex((m) => m.id === delta.market.id);
|
const index = draft.findIndex((m) => m.id === delta.market.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
draft[index].data = delta;
|
draft[index].data = delta;
|
||||||
}
|
}
|
||||||
// @TODO - else push new market to draft
|
// @TODO - else push new market to draft
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: SimpleMarkets) => responseData.markets;
|
const getData = (responseData: SimpleMarkets) => responseData.markets;
|
||||||
const getDelta = (
|
const getDelta = (
|
||||||
|
@ -16,11 +16,11 @@ import { ThemeContext } from '@vegaprotocol/react-helpers';
|
|||||||
import type { MarketState } from '@vegaprotocol/types';
|
import type { MarketState } from '@vegaprotocol/types';
|
||||||
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
|
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
|
||||||
import useColumnDefinitions from '../../hooks/use-column-definitions';
|
import useColumnDefinitions from '../../hooks/use-column-definitions';
|
||||||
import DataProvider from './data-provider';
|
import dataProvider from './data-provider';
|
||||||
import * as constants from './constants';
|
import * as constants from './constants';
|
||||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||||
|
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
|
||||||
export type SimpleMarketsType = SimpleMarkets_markets & {
|
export type SimpleMarketsType = SimpleMarkets_markets & {
|
||||||
percentChange?: number | '-';
|
percentChange?: number | '-';
|
||||||
};
|
};
|
||||||
@ -44,15 +44,16 @@ const SimpleMarketList = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(delta) => statusesRef.current[delta.market.id] === delta.market.state,
|
({ delta }: { delta: SimpleMarketDataSub_marketData }) =>
|
||||||
|
statusesRef.current[delta.market.id] === delta.market.state,
|
||||||
[statusesRef]
|
[statusesRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error, loading } = useDataProvider(
|
const { data, error, loading } = useDataProvider({
|
||||||
DataProvider,
|
dataProvider,
|
||||||
update,
|
update,
|
||||||
variables
|
variables,
|
||||||
);
|
});
|
||||||
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
|
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
|
||||||
data || [],
|
data || [],
|
||||||
params
|
params
|
||||||
|
@ -55,8 +55,8 @@ export const getId = (
|
|||||||
const update = (
|
const update = (
|
||||||
data: Accounts_party_accounts[],
|
data: Accounts_party_accounts[],
|
||||||
delta: AccountSubscribe_accounts
|
delta: AccountSubscribe_accounts
|
||||||
) =>
|
) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const id = getId(delta);
|
const id = getId(delta);
|
||||||
const index = draft.findIndex((a) => getId(a) === id);
|
const index = draft.findIndex((a) => getId(a) === id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
@ -65,6 +65,8 @@ const update = (
|
|||||||
draft.push(delta);
|
draft.push(delta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: Accounts): Accounts_party_accounts[] | null =>
|
const getData = (responseData: Accounts): Accounts_party_accounts[] | null =>
|
||||||
responseData.party ? responseData.party.accounts : null;
|
responseData.party ? responseData.party.accounts : null;
|
||||||
const getDelta = (
|
const getDelta = (
|
||||||
|
@ -22,7 +22,7 @@ export const AccountsManager = ({ partyId }: AccountsManagerProps) => {
|
|||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(delta: AccountSubscribe_accounts) => {
|
({ delta }: { delta: AccountSubscribe_accounts }) => {
|
||||||
const update: Accounts_party_accounts[] = [];
|
const update: Accounts_party_accounts[] = [];
|
||||||
const add: Accounts_party_accounts[] = [];
|
const add: Accounts_party_accounts[] = [];
|
||||||
if (!gridRef.current) {
|
if (!gridRef.current) {
|
||||||
@ -64,7 +64,7 @@ export const AccountsManager = ({ partyId }: AccountsManagerProps) => {
|
|||||||
const { data, error, loading } = useDataProvider<
|
const { data, error, loading } = useDataProvider<
|
||||||
Accounts_party_accounts[],
|
Accounts_party_accounts[],
|
||||||
AccountSubscribe_accounts
|
AccountSubscribe_accounts
|
||||||
>(accountsDataProvider, update, variables);
|
>({ dataProvider: accountsDataProvider, update, variables });
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
<AccountsTable ref={gridRef} data={data} />
|
<AccountsTable ref={gridRef} data={data} />
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import produce from 'immer';
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import produce from 'immer';
|
import type { PageInfo, Pagination } from '@vegaprotocol/react-helpers';
|
||||||
import type { FillFields } from './__generated__/FillFields';
|
import type { FillFields } from './__generated__/FillFields';
|
||||||
import type {
|
import type {
|
||||||
Fills,
|
Fills,
|
||||||
Fills_party_tradesPaged_edges_node,
|
Fills_party_tradesPaged_edges,
|
||||||
} from './__generated__/Fills';
|
} from './__generated__/Fills';
|
||||||
import type { FillsSub } from './__generated__/FillsSub';
|
import type { FillsSub } from './__generated__/FillsSub';
|
||||||
|
|
||||||
@ -16,41 +17,19 @@ const FILL_FRAGMENT = gql`
|
|||||||
size
|
size
|
||||||
buyOrder
|
buyOrder
|
||||||
sellOrder
|
sellOrder
|
||||||
aggressor
|
|
||||||
buyer {
|
buyer {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
seller {
|
seller {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
buyerFee {
|
|
||||||
makerFee
|
|
||||||
infrastructureFee
|
|
||||||
liquidityFee
|
|
||||||
}
|
|
||||||
sellerFee {
|
|
||||||
makerFee
|
|
||||||
infrastructureFee
|
|
||||||
liquidityFee
|
|
||||||
}
|
|
||||||
market {
|
market {
|
||||||
id
|
id
|
||||||
name
|
|
||||||
decimalPlaces
|
decimalPlaces
|
||||||
positionDecimalPlaces
|
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
id
|
id
|
||||||
code
|
code
|
||||||
product {
|
|
||||||
... on Future {
|
|
||||||
settlementAsset {
|
|
||||||
id
|
|
||||||
symbol
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,30 +67,69 @@ export const FILLS_SUB = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const update = (data: FillFields[], delta: FillFields[]) => {
|
const update = (data: Fills_party_tradesPaged_edges[], delta: FillFields[]) => {
|
||||||
// Add or update incoming trades
|
|
||||||
return produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
delta.forEach((trade) => {
|
delta.forEach((node) => {
|
||||||
const index = draft.findIndex((t) => t.id === trade.id);
|
const index = draft.findIndex((edge) => edge.node.id === node.id);
|
||||||
if (index === -1) {
|
if (index !== -1) {
|
||||||
draft.unshift(trade);
|
Object.assign(draft[index].node, node);
|
||||||
} else {
|
} else {
|
||||||
draft[index] = trade;
|
draft.unshift({ node, cursor: '', __typename: 'TradeEdge' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getData = (
|
const getData = (responseData: Fills): Fills_party_tradesPaged_edges[] | null =>
|
||||||
responseData: Fills
|
responseData.party?.tradesPaged.edges || null;
|
||||||
): Fills_party_tradesPaged_edges_node[] | null =>
|
|
||||||
responseData.party?.tradesPaged.edges.map((e) => e.node) || null;
|
const getPageInfo = (responseData: Fills): PageInfo | null =>
|
||||||
|
responseData.party?.tradesPaged.pageInfo || null;
|
||||||
|
|
||||||
|
const getTotalCount = (responseData: Fills): number | undefined =>
|
||||||
|
responseData.party?.tradesPaged.totalCount;
|
||||||
|
|
||||||
const getDelta = (subscriptionData: FillsSub) => subscriptionData.trades || [];
|
const getDelta = (subscriptionData: FillsSub) => subscriptionData.trades || [];
|
||||||
|
|
||||||
|
const append = (
|
||||||
|
data: Fills_party_tradesPaged_edges[] | null,
|
||||||
|
pageInfo: PageInfo,
|
||||||
|
insertionData: Fills_party_tradesPaged_edges[] | null,
|
||||||
|
insertionPageInfo: PageInfo | null,
|
||||||
|
pagination?: Pagination
|
||||||
|
) => {
|
||||||
|
if (data && insertionData && insertionPageInfo) {
|
||||||
|
if (pagination?.after) {
|
||||||
|
if (data[data.length - 1].cursor === pagination.after) {
|
||||||
|
return {
|
||||||
|
data: [...data, ...insertionData],
|
||||||
|
pageInfo: { ...pageInfo, endCursor: insertionPageInfo.endCursor },
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const cursors = data.map((item) => item.cursor);
|
||||||
|
const startIndex = cursors.lastIndexOf(pagination.after);
|
||||||
|
if (startIndex !== -1) {
|
||||||
|
return {
|
||||||
|
data: [...data.slice(0, startIndex), ...insertionData],
|
||||||
|
pageInfo: { ...pageInfo, endCursor: insertionPageInfo.endCursor },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { data, pageInfo };
|
||||||
|
};
|
||||||
|
|
||||||
export const fillsDataProvider = makeDataProvider(
|
export const fillsDataProvider = makeDataProvider(
|
||||||
FILLS_QUERY,
|
FILLS_QUERY,
|
||||||
FILLS_SUB,
|
FILLS_SUB,
|
||||||
update,
|
update,
|
||||||
getData,
|
getData,
|
||||||
getDelta
|
getDelta,
|
||||||
|
{
|
||||||
|
getPageInfo,
|
||||||
|
getTotalCount,
|
||||||
|
append,
|
||||||
|
first: 100,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import { useCallback, useMemo, useRef } from 'react';
|
import { useCallback, useRef, useMemo } from 'react';
|
||||||
import { FillsTable } from './fills-table';
|
|
||||||
import { fillsDataProvider } from './fills-data-provider';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { FillsVariables } from './__generated__/Fills';
|
import { FillsTable } from './fills-table';
|
||||||
import type { FillFields } from './__generated__/FillFields';
|
import type { IGetRowsParams } from 'ag-grid-community';
|
||||||
|
|
||||||
|
import { fillsDataProvider as dataProvider } from './fills-data-provider';
|
||||||
|
import type { Fills_party_tradesPaged_edges } from './__generated__/Fills';
|
||||||
import type { FillsSub_trades } from './__generated__/FillsSub';
|
import type { FillsSub_trades } from './__generated__/FillsSub';
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
|
|
||||||
interface FillsManagerProps {
|
interface FillsManagerProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
@ -15,66 +15,78 @@ interface FillsManagerProps {
|
|||||||
|
|
||||||
export const FillsManager = ({ partyId }: FillsManagerProps) => {
|
export const FillsManager = ({ partyId }: FillsManagerProps) => {
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const variables = useMemo<FillsVariables>(
|
const dataRef = useRef<Fills_party_tradesPaged_edges[] | null>(null);
|
||||||
() => ({
|
const totalCountRef = useRef<number | undefined>(undefined);
|
||||||
partyId,
|
|
||||||
pagination: {
|
const update = useCallback(
|
||||||
last: 300,
|
({ data }: { data: Fills_party_tradesPaged_edges[] }) => {
|
||||||
},
|
if (!gridRef.current?.api) {
|
||||||
}),
|
|
||||||
[partyId]
|
|
||||||
);
|
|
||||||
const update = useCallback((delta: FillsSub_trades[]) => {
|
|
||||||
if (!gridRef.current) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const updateRows: FillFields[] = [];
|
dataRef.current = data;
|
||||||
const add: FillFields[] = [];
|
gridRef.current.api.refreshInfiniteCache();
|
||||||
|
|
||||||
delta.forEach((d) => {
|
|
||||||
if (!gridRef.current?.api) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowNode = gridRef.current.api.getRowNode(d.id);
|
|
||||||
|
|
||||||
if (rowNode) {
|
|
||||||
if (!isEqual(d, rowNode.data)) {
|
|
||||||
updateRows.push(d);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
add.push(d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (updateRows.length || add.length) {
|
|
||||||
gridRef.current.api.applyTransactionAsync({
|
|
||||||
update: updateRows,
|
|
||||||
add,
|
|
||||||
addIndex: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
const { data, loading, error } = useDataProvider(
|
|
||||||
fillsDataProvider,
|
|
||||||
update,
|
|
||||||
variables
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const fills = useMemo(() => {
|
const insert = useCallback(
|
||||||
if (!data?.length) {
|
({
|
||||||
return [];
|
data,
|
||||||
}
|
totalCount,
|
||||||
|
}: {
|
||||||
|
data: Fills_party_tradesPaged_edges[];
|
||||||
|
totalCount?: number;
|
||||||
|
}) => {
|
||||||
|
dataRef.current = data;
|
||||||
|
totalCountRef.current = totalCount;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return data;
|
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||||
}, [data]);
|
|
||||||
|
const { data, error, loading, load, totalCount } = useDataProvider<
|
||||||
|
Fills_party_tradesPaged_edges[],
|
||||||
|
FillsSub_trades[]
|
||||||
|
>({ dataProvider, update, insert, variables });
|
||||||
|
totalCountRef.current = totalCount;
|
||||||
|
dataRef.current = data;
|
||||||
|
|
||||||
|
const getRows = async ({
|
||||||
|
successCallback,
|
||||||
|
failCallback,
|
||||||
|
startRow,
|
||||||
|
endRow,
|
||||||
|
}: IGetRowsParams) => {
|
||||||
|
try {
|
||||||
|
if (dataRef.current && dataRef.current.length < endRow) {
|
||||||
|
await load({
|
||||||
|
first: endRow - startRow,
|
||||||
|
after: dataRef.current[dataRef.current.length - 1].cursor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const rowsThisBlock = dataRef.current
|
||||||
|
? dataRef.current.slice(startRow, endRow).map((edge) => edge.node)
|
||||||
|
: [];
|
||||||
|
let lastRow = -1;
|
||||||
|
if (totalCountRef.current !== undefined) {
|
||||||
|
if (!totalCountRef.current) {
|
||||||
|
lastRow = 0;
|
||||||
|
} else if (totalCountRef.current <= endRow) {
|
||||||
|
lastRow = totalCountRef.current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
successCallback(rowsThisBlock, lastRow);
|
||||||
|
} catch (e) {
|
||||||
|
failCallback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer data={fills} loading={loading} error={error}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
<FillsTable ref={gridRef} partyId={partyId} fills={fills} />
|
<FillsTable ref={gridRef} partyId={partyId} datasource={{ getRows }} />
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
import { render, act, screen, waitFor } from '@testing-library/react';
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
import { FillsTable } from './fills-table';
|
import { FillsTable } from './fills-table';
|
||||||
import { generateFill } from './test-helpers';
|
import { generateFill, makeGetRows } from './test-helpers';
|
||||||
import type { FillFields } from './__generated__/FillFields';
|
import type { FillFields } from './__generated__/FillFields';
|
||||||
|
|
||||||
|
const waitForGridToBeInTheDOM = () => {
|
||||||
|
return waitFor(() => {
|
||||||
|
expect(document.querySelector('.ag-root-wrapper')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// since our grid starts with no data, when the overlay has gone, data has loaded
|
||||||
|
const waitForDataToHaveLoaded = () => {
|
||||||
|
return waitFor(() => {
|
||||||
|
expect(document.querySelector('.ag-overlay-no-rows-center')).toBeNull();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
describe('FillsTable', () => {
|
describe('FillsTable', () => {
|
||||||
let defaultFill: PartialDeep<FillFields>;
|
let defaultFill: PartialDeep<FillFields>;
|
||||||
|
|
||||||
@ -34,9 +47,14 @@ describe('FillsTable', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('correct columns are rendered', async () => {
|
it('correct columns are rendered', async () => {
|
||||||
await act(async () => {
|
render(
|
||||||
render(<FillsTable partyId="party-id" fills={[generateFill()]} />);
|
<FillsTable
|
||||||
});
|
partyId="party-id"
|
||||||
|
datasource={{ getRows: makeGetRows([generateFill()]) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await waitForGridToBeInTheDOM();
|
||||||
|
await waitForDataToHaveLoaded();
|
||||||
|
|
||||||
const headers = screen.getAllByRole('columnheader');
|
const headers = screen.getAllByRole('columnheader');
|
||||||
expect(headers).toHaveLength(7);
|
expect(headers).toHaveLength(7);
|
||||||
@ -66,14 +84,14 @@ describe('FillsTable', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { container } = render(
|
render(
|
||||||
<FillsTable partyId={partyId} fills={[buyerFill]} />
|
<FillsTable
|
||||||
|
partyId={partyId}
|
||||||
|
datasource={{ getRows: makeGetRows([buyerFill]) }}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
await waitForGridToBeInTheDOM();
|
||||||
// Check grid has been rendered
|
await waitForDataToHaveLoaded();
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
const expectedValues = [
|
const expectedValues = [
|
||||||
@ -108,14 +126,14 @@ describe('FillsTable', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { container } = render(
|
render(
|
||||||
<FillsTable partyId={partyId} fills={[buyerFill]} />
|
<FillsTable
|
||||||
|
partyId={partyId}
|
||||||
|
datasource={{ getRows: makeGetRows([buyerFill]) }}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
await waitForGridToBeInTheDOM();
|
||||||
// Check grid has been rendered
|
await waitForDataToHaveLoaded();
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
const expectedValues = [
|
const expectedValues = [
|
||||||
@ -144,14 +162,14 @@ describe('FillsTable', () => {
|
|||||||
aggressor: Side.Sell,
|
aggressor: Side.Sell,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { container, rerender } = render(
|
const { rerender } = render(
|
||||||
<FillsTable partyId={partyId} fills={[takerFill]} />
|
<FillsTable
|
||||||
|
partyId={partyId}
|
||||||
|
datasource={{ getRows: makeGetRows([takerFill]) }}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
await waitForGridToBeInTheDOM();
|
||||||
// Check grid has been rendered
|
await waitForDataToHaveLoaded();
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen
|
screen
|
||||||
@ -166,7 +184,14 @@ describe('FillsTable', () => {
|
|||||||
aggressor: Side.Buy,
|
aggressor: Side.Buy,
|
||||||
});
|
});
|
||||||
|
|
||||||
rerender(<FillsTable partyId={partyId} fills={[makerFill]} />);
|
rerender(
|
||||||
|
<FillsTable
|
||||||
|
partyId={partyId}
|
||||||
|
datasource={{ getRows: makeGetRows([makerFill]) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await waitForGridToBeInTheDOM();
|
||||||
|
await waitForDataToHaveLoaded();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen
|
screen
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { Story, Meta } from '@storybook/react';
|
import type { Story, Meta } from '@storybook/react';
|
||||||
import type { FillsTableProps } from './fills-table';
|
import type { FillsTableProps } from './fills-table';
|
||||||
import { FillsTable } from './fills-table';
|
import { FillsTable } from './fills-table';
|
||||||
import { generateFills } from './test-helpers';
|
import { generateFills, makeGetRows } from './test-helpers';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: FillsTable,
|
component: FillsTable,
|
||||||
@ -14,5 +14,9 @@ export const Default = Template.bind({});
|
|||||||
const fills = generateFills();
|
const fills = generateFills();
|
||||||
Default.args = {
|
Default.args = {
|
||||||
partyId: 'party-id',
|
partyId: 'party-id',
|
||||||
fills: fills.party?.tradesPaged.edges.map((e) => e.node),
|
datasource: {
|
||||||
|
getRows: makeGetRows(
|
||||||
|
fills.party?.tradesPaged.edges.map((e) => e.node) || []
|
||||||
|
),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -10,25 +10,26 @@ import { AgGridColumn } from 'ag-grid-react';
|
|||||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import type { FillFields } from './__generated__/FillFields';
|
import type { FillFields } from './__generated__/FillFields';
|
||||||
import type { ValueFormatterParams } from 'ag-grid-community';
|
import type { ValueFormatterParams, IDatasource } from 'ag-grid-community';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export interface FillsTableProps {
|
export interface FillsTableProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
fills: FillFields[];
|
datasource: IDatasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FillsTable = forwardRef<AgGridReact, FillsTableProps>(
|
export const FillsTable = forwardRef<AgGridReact, FillsTableProps>(
|
||||||
({ partyId, fills }, ref) => {
|
({ partyId, datasource }, ref) => {
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
ref={ref}
|
ref={ref}
|
||||||
rowData={fills}
|
|
||||||
overlayNoRowsTemplate={t('No fills')}
|
overlayNoRowsTemplate={t('No fills')}
|
||||||
defaultColDef={{ flex: 1, resizable: true }}
|
defaultColDef={{ flex: 1, resizable: true }}
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
getRowId={({ data }) => data.id}
|
getRowId={({ data }) => data?.id}
|
||||||
|
rowModelType="infinite"
|
||||||
|
datasource={datasource}
|
||||||
>
|
>
|
||||||
<AgGridColumn headerName={t('Market')} field="market.name" />
|
<AgGridColumn headerName={t('Market')} field="market.name" />
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -36,9 +37,9 @@ export const FillsTable = forwardRef<AgGridReact, FillsTableProps>(
|
|||||||
field="size"
|
field="size"
|
||||||
cellClass={({ data }: { data: FillFields }) => {
|
cellClass={({ data }: { data: FillFields }) => {
|
||||||
let className = '';
|
let className = '';
|
||||||
if (data.buyer.id === partyId) {
|
if (data?.buyer.id === partyId) {
|
||||||
className = 'text-vega-green';
|
className = 'text-vega-green';
|
||||||
} else if (data.seller.id) {
|
} else if (data?.seller.id) {
|
||||||
className = 'text-vega-red';
|
className = 'text-vega-red';
|
||||||
}
|
}
|
||||||
return className;
|
return className;
|
||||||
@ -69,6 +70,9 @@ export const FillsTable = forwardRef<AgGridReact, FillsTableProps>(
|
|||||||
headerName={t('Date')}
|
headerName={t('Date')}
|
||||||
field="createdAt"
|
field="createdAt"
|
||||||
valueFormatter={({ value }: ValueFormatterParams) => {
|
valueFormatter={({ value }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
return getDateTimeFormat().format(new Date(value));
|
return getDateTimeFormat().format(new Date(value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -78,56 +82,68 @@ export const FillsTable = forwardRef<AgGridReact, FillsTableProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const formatPrice = ({ value, data }: ValueFormatterParams) => {
|
const formatPrice = ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
const asset =
|
const asset =
|
||||||
data.market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
data?.market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||||
const valueFormatted = addDecimalsFormatNumber(
|
const valueFormatted = addDecimalsFormatNumber(
|
||||||
value,
|
value,
|
||||||
data.market.decimalPlaces
|
data?.market.decimalPlaces
|
||||||
);
|
);
|
||||||
return `${valueFormatted} ${asset}`;
|
return `${valueFormatted} ${asset}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatSize = (partyId: string) => {
|
const formatSize = (partyId: string) => {
|
||||||
return ({ value, data }: ValueFormatterParams) => {
|
return ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
let prefix;
|
let prefix;
|
||||||
if (data.buyer.id === partyId) {
|
if (data?.buyer.id === partyId) {
|
||||||
prefix = '+';
|
prefix = '+';
|
||||||
} else if (data.seller.id) {
|
} else if (data?.seller.id) {
|
||||||
prefix = '-';
|
prefix = '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
const size = addDecimalsFormatNumber(
|
const size = addDecimalsFormatNumber(
|
||||||
value,
|
value,
|
||||||
data.market.positionDecimalPlaces
|
data?.market.positionDecimalPlaces
|
||||||
);
|
);
|
||||||
return `${prefix}${size}`;
|
return `${prefix}${size}`;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTotal = ({ value, data }: ValueFormatterParams) => {
|
const formatTotal = ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
const asset =
|
const asset =
|
||||||
data.market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
data?.market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||||
const size = new BigNumber(
|
const size = new BigNumber(
|
||||||
addDecimal(data.size, data.market.positionDecimalPlaces)
|
addDecimal(data?.size, data?.market.positionDecimalPlaces)
|
||||||
);
|
);
|
||||||
const price = new BigNumber(addDecimal(value, data.market.decimalPlaces));
|
const price = new BigNumber(addDecimal(value, data?.market.decimalPlaces));
|
||||||
|
|
||||||
const total = size.times(price).toString();
|
const total = size.times(price).toString();
|
||||||
const valueFormatted = formatNumber(total, data.market.decimalPlaces);
|
const valueFormatted = formatNumber(total, data?.market.decimalPlaces);
|
||||||
return `${valueFormatted} ${asset}`;
|
return `${valueFormatted} ${asset}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatRole = (partyId: string) => {
|
const formatRole = (partyId: string) => {
|
||||||
return ({ value, data }: ValueFormatterParams) => {
|
return ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
const taker = t('Taker');
|
const taker = t('Taker');
|
||||||
const maker = t('Maker');
|
const maker = t('Maker');
|
||||||
if (data.buyer.id === partyId) {
|
if (data?.buyer.id === partyId) {
|
||||||
if (value === Side.Buy) {
|
if (value === Side.Buy) {
|
||||||
return taker;
|
return taker;
|
||||||
} else {
|
} else {
|
||||||
return maker;
|
return maker;
|
||||||
}
|
}
|
||||||
} else if (data.seller.id === partyId) {
|
} else if (data?.seller.id === partyId) {
|
||||||
if (value === Side.Sell) {
|
if (value === Side.Sell) {
|
||||||
return taker;
|
return taker;
|
||||||
} else {
|
} else {
|
||||||
@ -141,12 +157,15 @@ const formatRole = (partyId: string) => {
|
|||||||
|
|
||||||
const formatFee = (partyId: string) => {
|
const formatFee = (partyId: string) => {
|
||||||
return ({ value, data }: ValueFormatterParams) => {
|
return ({ value, data }: ValueFormatterParams) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
const asset = value.settlementAsset;
|
const asset = value.settlementAsset;
|
||||||
let feesObj;
|
let feesObj;
|
||||||
if (data.buyer.id === partyId) {
|
if (data?.buyer.id === partyId) {
|
||||||
feesObj = data.buyerFee;
|
feesObj = data?.buyerFee;
|
||||||
} else if (data.seller.id === partyId) {
|
} else if (data?.seller.id === partyId) {
|
||||||
feesObj = data.sellerFee;
|
feesObj = data?.sellerFee;
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
|
import type { IGetRowsParams } from 'ag-grid-community';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type {
|
import type {
|
||||||
Fills,
|
Fills,
|
||||||
@ -132,3 +133,9 @@ export const generateFill = (
|
|||||||
|
|
||||||
return merge(defaultFill, override);
|
return merge(defaultFill, override);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const makeGetRows =
|
||||||
|
(data: Fills_party_tradesPaged_edges_node[]) =>
|
||||||
|
({ successCallback }: IGetRowsParams) => {
|
||||||
|
successCallback(data, data.length);
|
||||||
|
};
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
addDecimal,
|
addDecimal,
|
||||||
ThemeContext,
|
ThemeContext,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { marketDepthDataProvider } from './market-depth-data-provider';
|
import dataProvider from './market-depth-data-provider';
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
@ -87,7 +87,7 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
|
|||||||
|
|
||||||
// Apply updates to the table
|
// Apply updates to the table
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(delta: MarketDepthSubscription_marketDepthUpdate) => {
|
({ delta }: { delta: MarketDepthSubscription_marketDepthUpdate }) => {
|
||||||
if (!dataRef.current) {
|
if (!dataRef.current) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -122,11 +122,11 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error, loading } = useDataProvider(
|
const { data, error, loading } = useDataProvider({
|
||||||
marketDepthDataProvider,
|
dataProvider,
|
||||||
update,
|
update,
|
||||||
variables
|
variables,
|
||||||
);
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -131,3 +131,5 @@ export const marketDepthDataProvider = makeDataProvider(
|
|||||||
getData,
|
getData,
|
||||||
getDelta
|
getDelta
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export default marketDepthDataProvider;
|
||||||
|
@ -150,9 +150,9 @@ export const compactRows = (
|
|||||||
groupedByLevel[price].pop() as PartialOrderbookRowData
|
groupedByLevel[price].pop() as PartialOrderbookRowData
|
||||||
);
|
);
|
||||||
row.price = price;
|
row.price = price;
|
||||||
let subRow: PartialOrderbookRowData | undefined;
|
let subRow: PartialOrderbookRowData | undefined =
|
||||||
// eslint-disable-next-line no-cond-assign
|
groupedByLevel[price].pop();
|
||||||
while ((subRow = groupedByLevel[price].pop())) {
|
while (subRow) {
|
||||||
row.ask += subRow.ask;
|
row.ask += subRow.ask;
|
||||||
row.bid += subRow.bid;
|
row.bid += subRow.bid;
|
||||||
if (subRow.ask) {
|
if (subRow.ask) {
|
||||||
@ -161,6 +161,7 @@ export const compactRows = (
|
|||||||
if (subRow.bid) {
|
if (subRow.bid) {
|
||||||
row.bidByLevel[subRow.price] = subRow.bid;
|
row.bidByLevel[subRow.price] = subRow.bid;
|
||||||
}
|
}
|
||||||
|
subRow = groupedByLevel[price].pop();
|
||||||
}
|
}
|
||||||
orderbookData.push(row);
|
orderbookData.push(row);
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@ import throttle from 'lodash/throttle';
|
|||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Orderbook } from './orderbook';
|
import { Orderbook } from './orderbook';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import { marketDepthDataProvider } from './market-depth-data-provider';
|
import dataProvider from './market-depth-data-provider';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import type { MarketDepthSubscription_marketDepthUpdate } from './__generated__/MarketDepthSubscription';
|
import type { MarketDepthSubscription_marketDepthUpdate } from './__generated__/MarketDepthSubscription';
|
||||||
import {
|
import {
|
||||||
@ -46,7 +46,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(delta: MarketDepthSubscription_marketDepthUpdate) => {
|
({ delta }: { delta: MarketDepthSubscription_marketDepthUpdate }) => {
|
||||||
if (!dataRef.current.rows) {
|
if (!dataRef.current.rows) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -76,11 +76,11 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error, loading, flush } = useDataProvider(
|
const { data, error, loading, flush } = useDataProvider({
|
||||||
marketDepthDataProvider,
|
dataProvider,
|
||||||
update,
|
update,
|
||||||
variables
|
variables,
|
||||||
);
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import type { ValueFormatterParams } from 'ag-grid-community';
|
import type { IDatasource, ValueFormatterParams } from 'ag-grid-community';
|
||||||
import {
|
import {
|
||||||
PriceFlashCell,
|
PriceFlashCell,
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
@ -8,30 +8,22 @@ import {
|
|||||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
import { AgGridColumn } from 'ag-grid-react';
|
import { AgGridColumn } from 'ag-grid-react';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import type {
|
import type { Markets_markets } from '../__generated__/Markets';
|
||||||
Markets_markets,
|
|
||||||
Markets_markets_data_market,
|
|
||||||
} from '../__generated__/Markets';
|
|
||||||
|
|
||||||
interface MarketListTableProps {
|
interface MarketListTableProps {
|
||||||
data: Markets_markets[] | null;
|
datasource: IDatasource;
|
||||||
onRowClicked: (marketId: string) => void;
|
onRowClicked: (marketId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRowId = ({
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
data: Markets_markets | Markets_markets_data_market;
|
|
||||||
}) => data.id;
|
|
||||||
|
|
||||||
export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
||||||
({ data, onRowClicked }, ref) => {
|
({ datasource, onRowClicked }, ref) => {
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
overlayNoRowsTemplate={t('No markets')}
|
overlayNoRowsTemplate={t('No markets')}
|
||||||
rowData={data}
|
rowModelType="infinite"
|
||||||
getRowId={getRowId}
|
datasource={datasource}
|
||||||
|
getRowId={({ data }) => data?.id}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
defaultColDef={{
|
defaultColDef={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -55,7 +47,9 @@ export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
|||||||
headerName={t('State')}
|
headerName={t('State')}
|
||||||
field="data"
|
field="data"
|
||||||
valueFormatter={({ value }: ValueFormatterParams) =>
|
valueFormatter={({ value }: ValueFormatterParams) =>
|
||||||
`${value.market.state} (${value.market.tradingMode})`
|
value === undefined
|
||||||
|
? value
|
||||||
|
: `${value.market.state} (${value.market.tradingMode})`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -64,7 +58,9 @@ export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
|||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
cellRenderer="PriceFlashCell"
|
cellRenderer="PriceFlashCell"
|
||||||
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
||||||
addDecimalsFormatNumber(value, data.decimalPlaces)
|
value === undefined
|
||||||
|
? value
|
||||||
|
: addDecimalsFormatNumber(value, data.decimalPlaces)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -72,7 +68,9 @@ export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
|||||||
field="data.bestOfferPrice"
|
field="data.bestOfferPrice"
|
||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
||||||
addDecimalsFormatNumber(value, data.decimalPlaces)
|
value === undefined
|
||||||
|
? value
|
||||||
|
: addDecimalsFormatNumber(value, data.decimalPlaces)
|
||||||
}
|
}
|
||||||
cellRenderer="PriceFlashCell"
|
cellRenderer="PriceFlashCell"
|
||||||
/>
|
/>
|
||||||
@ -82,7 +80,9 @@ export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
|||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
cellRenderer="PriceFlashCell"
|
cellRenderer="PriceFlashCell"
|
||||||
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
valueFormatter={({ value, data }: ValueFormatterParams) =>
|
||||||
addDecimalsFormatNumber(value, data.decimalPlaces)
|
value === undefined
|
||||||
|
? value
|
||||||
|
: addDecimalsFormatNumber(value, data.decimalPlaces)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn headerName={t('Description')} field="name" />
|
<AgGridColumn headerName={t('Description')} field="name" />
|
||||||
|
@ -1,61 +1,51 @@
|
|||||||
import { useRef, useCallback } from 'react';
|
import { useRef, useCallback } from 'react';
|
||||||
import { produce } from 'immer';
|
|
||||||
import merge from 'lodash/merge';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { MarketListTable, getRowId } from './market-list-table';
|
import { MarketListTable } from './market-list-table';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import type { IGetRowsParams } from 'ag-grid-community';
|
||||||
import type {
|
import type {
|
||||||
Markets_markets,
|
Markets_markets,
|
||||||
Markets_markets_data,
|
Markets_markets_data,
|
||||||
} from '../../components/__generated__/Markets';
|
} from '../../components/__generated__/Markets';
|
||||||
import { marketsDataProvider } from './markets-data-provider';
|
import { marketsDataProvider as dataProvider } from './markets-data-provider';
|
||||||
|
|
||||||
export const MarketsContainer = () => {
|
export const MarketsContainer = () => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const update = useCallback(
|
const dataRef = useRef<Markets_markets[] | null>(null);
|
||||||
(delta: Markets_markets_data) => {
|
const update = useCallback(({ data }: { data: Markets_markets[] }) => {
|
||||||
const update: Markets_markets[] = [];
|
|
||||||
const add: Markets_markets[] = [];
|
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const rowNode = gridRef.current.api.getRowNode(
|
dataRef.current = data;
|
||||||
getRowId({ data: delta.market })
|
gridRef.current.api.refreshInfiniteCache();
|
||||||
);
|
|
||||||
if (rowNode) {
|
|
||||||
const updatedData = produce<Markets_markets_data>(
|
|
||||||
rowNode.data.data,
|
|
||||||
(draft: Markets_markets_data) => merge(draft, delta)
|
|
||||||
);
|
|
||||||
if (updatedData !== rowNode.data.data) {
|
|
||||||
update.push({ ...rowNode.data, data: updatedData });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// @TODO - else add new market
|
|
||||||
if (update.length || add.length) {
|
|
||||||
gridRef.current.api.applyTransactionAsync({
|
|
||||||
update,
|
|
||||||
add,
|
|
||||||
addIndex: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
},
|
}, []);
|
||||||
[gridRef]
|
|
||||||
);
|
|
||||||
const { data, error, loading } = useDataProvider<
|
const { data, error, loading } = useDataProvider<
|
||||||
Markets_markets[],
|
Markets_markets[],
|
||||||
Markets_markets_data
|
Markets_markets_data
|
||||||
>(marketsDataProvider, update);
|
>({ dataProvider, update });
|
||||||
|
dataRef.current = data;
|
||||||
|
|
||||||
|
const getRows = async ({
|
||||||
|
successCallback,
|
||||||
|
startRow,
|
||||||
|
endRow,
|
||||||
|
}: IGetRowsParams) => {
|
||||||
|
const rowsThisBlock = dataRef.current
|
||||||
|
? dataRef.current.slice(startRow, endRow)
|
||||||
|
: [];
|
||||||
|
const lastRow = dataRef.current?.length ?? -1;
|
||||||
|
successCallback(rowsThisBlock, lastRow);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
<MarketListTable
|
<MarketListTable
|
||||||
|
datasource={{ getRows }}
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
data={data}
|
|
||||||
onRowClicked={(id) => push(`/markets/${id}`)}
|
onRowClicked={(id) => push(`/markets/${id}`)}
|
||||||
/>
|
/>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
|
@ -91,14 +91,16 @@ const MARKET_DATA_SUB = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const update = (data: Markets_markets[], delta: MarketDataSub_marketData) =>
|
const update = (data: Markets_markets[], delta: MarketDataSub_marketData) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const index = draft.findIndex((m) => m.id === delta.market.id);
|
const index = draft.findIndex((m) => m.id === delta.market.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
draft[index].data = delta;
|
draft[index].data = delta;
|
||||||
}
|
}
|
||||||
// @TODO - else push new market to draft
|
// @TODO - else push new market to draft
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: Markets): Markets_markets[] | null =>
|
const getData = (responseData: Markets): Markets_markets[] | null =>
|
||||||
responseData.markets;
|
responseData.markets;
|
||||||
const getDelta = (subscriptionData: MarketDataSub): MarketDataSub_marketData =>
|
const getDelta = (subscriptionData: MarketDataSub): MarketDataSub_marketData =>
|
||||||
|
@ -79,8 +79,8 @@ export const prepareIncomingOrders = (delta: OrderFields[]) => {
|
|||||||
return incoming;
|
return incoming;
|
||||||
};
|
};
|
||||||
|
|
||||||
const update = (data: OrderFields[], delta: OrderFields[]) =>
|
const update = (data: OrderFields[], delta: OrderFields[]) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const incoming = prepareIncomingOrders(delta);
|
const incoming = prepareIncomingOrders(delta);
|
||||||
|
|
||||||
// Add or update incoming orders
|
// Add or update incoming orders
|
||||||
@ -93,6 +93,7 @@ const update = (data: OrderFields[], delta: OrderFields[]) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: Orders): Orders_party_orders[] | null =>
|
const getData = (responseData: Orders): Orders_party_orders[] | null =>
|
||||||
responseData?.party?.orders || null;
|
responseData?.party?.orders || null;
|
||||||
|
@ -3,7 +3,7 @@ import { OrderList } from '../order-list';
|
|||||||
import type { OrderFields } from '../__generated__/OrderFields';
|
import type { OrderFields } from '../__generated__/OrderFields';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
import {
|
import {
|
||||||
ordersDataProvider,
|
ordersDataProvider as dataProvider,
|
||||||
prepareIncomingOrders,
|
prepareIncomingOrders,
|
||||||
sortOrders,
|
sortOrders,
|
||||||
} from '../order-data-provider';
|
} from '../order-data-provider';
|
||||||
@ -21,7 +21,7 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
|
|||||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||||
|
|
||||||
// Apply updates to the table
|
// Apply updates to the table
|
||||||
const update = useCallback((delta: OrderSub_orders[]) => {
|
const update = useCallback(({ delta }: { delta: OrderSub_orders[] }) => {
|
||||||
if (!gridRef.current) {
|
if (!gridRef.current) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -57,11 +57,11 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
|
|||||||
return true;
|
return true;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data, error, loading } = useDataProvider(
|
const { data, error, loading } = useDataProvider({
|
||||||
ordersDataProvider,
|
dataProvider,
|
||||||
update,
|
update,
|
||||||
variables
|
variables,
|
||||||
);
|
});
|
||||||
|
|
||||||
const orders = useMemo(() => {
|
const orders = useMemo(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -78,8 +78,8 @@ export const POSITIONS_SUB = gql`
|
|||||||
const update = (
|
const update = (
|
||||||
data: Positions_party_positions[],
|
data: Positions_party_positions[],
|
||||||
delta: PositionSubscribe_positions
|
delta: PositionSubscribe_positions
|
||||||
) =>
|
) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const index = draft.findIndex((m) => m.market.id === delta.market.id);
|
const index = draft.findIndex((m) => m.market.id === delta.market.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
draft[index] = delta;
|
draft[index] = delta;
|
||||||
@ -87,6 +87,8 @@ const update = (
|
|||||||
draft.push(delta);
|
draft.push(delta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: Positions): Positions_party_positions[] | null =>
|
const getData = (responseData: Positions): Positions_party_positions[] | null =>
|
||||||
responseData.party ? responseData.party.positions : null;
|
responseData.party ? responseData.party.positions : null;
|
||||||
const getDelta = (
|
const getDelta = (
|
||||||
|
@ -18,7 +18,7 @@ export const PositionsManager = ({ partyId }: PositionsManagerProps) => {
|
|||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(delta: PositionSubscribe_positions) => {
|
({ delta }: { delta: PositionSubscribe_positions }) => {
|
||||||
const update: Positions_party_positions[] = [];
|
const update: Positions_party_positions[] = [];
|
||||||
const add: Positions_party_positions[] = [];
|
const add: Positions_party_positions[] = [];
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
@ -52,7 +52,7 @@ export const PositionsManager = ({ partyId }: PositionsManagerProps) => {
|
|||||||
const { data, error, loading } = useDataProvider<
|
const { data, error, loading } = useDataProvider<
|
||||||
Positions_party_positions[],
|
Positions_party_positions[],
|
||||||
PositionSubscribe_positions
|
PositionSubscribe_positions
|
||||||
>(positionsDataProvider, update, variables);
|
>({ dataProvider: positionsDataProvider, update, variables });
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
<PositionsTable ref={gridRef} data={data} />
|
<PositionsTable ref={gridRef} data={data} />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import type { OperationVariables } from '@apollo/client';
|
import type { OperationVariables } from '@apollo/client';
|
||||||
import type { Subscribe } from '../lib/generic-data-provider';
|
import type { Subscribe, Pagination, Load } from '../lib/generic-data-provider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -10,17 +10,33 @@ import type { Subscribe } from '../lib/generic-data-provider';
|
|||||||
* @param variables optional
|
* @param variables optional
|
||||||
* @returns state: data, loading, error, methods: flush (pass updated data to update function without delta), restart: () => void}};
|
* @returns state: data, loading, error, methods: flush (pass updated data to update function without delta), restart: () => void}};
|
||||||
*/
|
*/
|
||||||
export function useDataProvider<Data, Delta>(
|
export function useDataProvider<Data, Delta>({
|
||||||
dataProvider: Subscribe<Data, Delta>,
|
dataProvider,
|
||||||
update?: (delta: Delta) => boolean,
|
update,
|
||||||
variables?: OperationVariables
|
insert,
|
||||||
) {
|
variables,
|
||||||
|
}: {
|
||||||
|
dataProvider: Subscribe<Data, Delta>;
|
||||||
|
update?: ({ delta, data }: { delta: Delta; data: Data }) => boolean;
|
||||||
|
insert?: ({
|
||||||
|
insertionData,
|
||||||
|
data,
|
||||||
|
totalCount,
|
||||||
|
}: {
|
||||||
|
insertionData: Data;
|
||||||
|
data: Data;
|
||||||
|
totalCount?: number;
|
||||||
|
}) => boolean;
|
||||||
|
variables?: OperationVariables;
|
||||||
|
}) {
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const [data, setData] = useState<Data | null>(null);
|
const [data, setData] = useState<Data | null>(null);
|
||||||
|
const [totalCount, setTotalCount] = useState<number>();
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [error, setError] = useState<Error | undefined>(undefined);
|
const [error, setError] = useState<Error | undefined>(undefined);
|
||||||
const flushRef = useRef<(() => void) | undefined>(undefined);
|
const flushRef = useRef<(() => void) | undefined>(undefined);
|
||||||
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
||||||
|
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
||||||
const initialized = useRef<boolean>(false);
|
const initialized = useRef<boolean>(false);
|
||||||
const flush = useCallback(() => {
|
const flush = useCallback(() => {
|
||||||
if (flushRef.current) {
|
if (flushRef.current) {
|
||||||
@ -32,30 +48,48 @@ export function useDataProvider<Data, Delta>(
|
|||||||
reloadRef.current(force);
|
reloadRef.current(force);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
const load = useCallback((pagination: Pagination) => {
|
||||||
|
if (loadRef.current) {
|
||||||
|
return loadRef.current(pagination);
|
||||||
|
}
|
||||||
|
return Promise.reject();
|
||||||
|
}, []);
|
||||||
const callback = useCallback(
|
const callback = useCallback(
|
||||||
({ data, error, loading, delta }) => {
|
({ data, error, loading, delta, insertionData, totalCount }) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
setLoading(loading);
|
setLoading(loading);
|
||||||
if (!error && !loading) {
|
if (!error && !loading) {
|
||||||
// if update function returns true it means that component handles updates
|
// 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
|
// component can use flush() which will call callback without delta and cause data state update
|
||||||
if (!initialized.current || !delta || !update || !update(delta)) {
|
if (initialized.current) {
|
||||||
|
if (delta && update && update({ delta, data })) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
insertionData &&
|
||||||
|
insert &&
|
||||||
|
insert({ insertionData, data, totalCount })
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
initialized.current = true;
|
initialized.current = true;
|
||||||
|
setTotalCount(totalCount);
|
||||||
setData(data);
|
setData(data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[update]
|
[update, insert]
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { unsubscribe, flush, reload } = dataProvider(
|
const { unsubscribe, flush, reload, load } = dataProvider(
|
||||||
callback,
|
callback,
|
||||||
client,
|
client,
|
||||||
variables
|
variables
|
||||||
);
|
);
|
||||||
flushRef.current = flush;
|
flushRef.current = flush;
|
||||||
reloadRef.current = reload;
|
reloadRef.current = reload;
|
||||||
|
loadRef.current = load;
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [client, initialized, dataProvider, callback, variables]);
|
}, [client, initialized, dataProvider, callback, variables]);
|
||||||
return { data, loading, error, flush, reload };
|
return { data, loading, error, flush, reload, load, totalCount };
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,30 @@ export interface UpdateCallback<Data, Delta> {
|
|||||||
data: Data | null;
|
data: Data | null;
|
||||||
error?: Error;
|
error?: Error;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
pageInfo: PageInfo | null;
|
||||||
delta?: Delta;
|
delta?: Delta;
|
||||||
|
insertionData?: Data | null;
|
||||||
|
totalCount?: number;
|
||||||
}): void;
|
}): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Load<Data> {
|
||||||
|
(pagination: Pagination): Promise<Data | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Pagination {
|
||||||
|
first?: number;
|
||||||
|
after?: string;
|
||||||
|
last?: number;
|
||||||
|
before?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageInfo {
|
||||||
|
startCursor?: string;
|
||||||
|
endCursor?: string;
|
||||||
|
hasNextPage?: boolean;
|
||||||
|
hasPreviousPage?: boolean;
|
||||||
|
}
|
||||||
export interface Subscribe<Data, Delta> {
|
export interface Subscribe<Data, Delta> {
|
||||||
(
|
(
|
||||||
callback: UpdateCallback<Data, Delta>,
|
callback: UpdateCallback<Data, Delta>,
|
||||||
@ -25,6 +46,7 @@ export interface Subscribe<Data, Delta> {
|
|||||||
unsubscribe: () => void;
|
unsubscribe: () => void;
|
||||||
reload: (forceReset?: boolean) => void;
|
reload: (forceReset?: boolean) => void;
|
||||||
flush: () => void;
|
flush: () => void;
|
||||||
|
load: Load<Data>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,8 +57,29 @@ export interface Update<Data, Delta> {
|
|||||||
(data: Data, delta: Delta, reload: (forceReset?: boolean) => void): Data;
|
(data: Data, delta: Delta, reload: (forceReset?: boolean) => void): Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Append<Data> {
|
||||||
|
(
|
||||||
|
data: Data | null,
|
||||||
|
pageInfo: PageInfo,
|
||||||
|
insertionData: Data | null,
|
||||||
|
insertionPageInfo: PageInfo | null,
|
||||||
|
pagination?: Pagination
|
||||||
|
): {
|
||||||
|
data: Data | null;
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface GetData<QueryData, Data> {
|
interface GetData<QueryData, Data> {
|
||||||
(subscriptionData: QueryData): Data | null;
|
(queryData: QueryData): Data | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetPageInfo<QueryData> {
|
||||||
|
(queryData: QueryData): PageInfo | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetTotalCount<QueryData> {
|
||||||
|
(queryData: QueryData): number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetDelta<SubscriptionData, Delta> {
|
interface GetDelta<SubscriptionData, Delta> {
|
||||||
@ -57,6 +100,12 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
update: Update<Data, Delta>,
|
update: Update<Data, Delta>,
|
||||||
getData: GetData<QueryData, Data>,
|
getData: GetData<QueryData, Data>,
|
||||||
getDelta: GetDelta<SubscriptionData, Delta>,
|
getDelta: GetDelta<SubscriptionData, Delta>,
|
||||||
|
pagination?: {
|
||||||
|
getPageInfo: GetPageInfo<QueryData>;
|
||||||
|
getTotalCount: GetTotalCount<QueryData>;
|
||||||
|
append: Append<Data>;
|
||||||
|
first: number;
|
||||||
|
},
|
||||||
fetchPolicy: FetchPolicy = 'no-cache'
|
fetchPolicy: FetchPolicy = 'no-cache'
|
||||||
): Subscribe<Data, Delta> {
|
): Subscribe<Data, Delta> {
|
||||||
// list of callbacks passed through subscribe call
|
// list of callbacks passed through subscribe call
|
||||||
@ -70,20 +119,60 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
let loading = false;
|
let loading = false;
|
||||||
let client: ApolloClient<object> | undefined = undefined;
|
let client: ApolloClient<object> | undefined = undefined;
|
||||||
let subscription: Subscription | undefined = undefined;
|
let subscription: Subscription | undefined = undefined;
|
||||||
|
let pageInfo: PageInfo | null = null;
|
||||||
|
let totalCount: number | undefined;
|
||||||
|
|
||||||
// notify single callback about current state, delta is passes optionally only if notify was invoked onNext
|
// notify single callback about current state, delta is passes optionally only if notify was invoked onNext
|
||||||
const notify = (callback: UpdateCallback<Data, Delta>, delta?: Delta) => {
|
const notify = (
|
||||||
|
callback: UpdateCallback<Data, Delta>,
|
||||||
|
dataUpdate?: { delta?: Delta; insertionData?: Data | null }
|
||||||
|
) => {
|
||||||
callback({
|
callback({
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
delta,
|
pageInfo,
|
||||||
|
totalCount,
|
||||||
|
...dataUpdate,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// notify all callbacks
|
// notify all callbacks
|
||||||
const notifyAll = (delta?: Delta) => {
|
const notifyAll = (dataUpdate?: {
|
||||||
callbacks.forEach((callback) => notify(callback, delta));
|
delta?: Delta;
|
||||||
|
insertionData?: Data | null;
|
||||||
|
}) => {
|
||||||
|
callbacks.forEach((callback) => notify(callback, dataUpdate));
|
||||||
|
};
|
||||||
|
|
||||||
|
const load = async (params?: Pagination) => {
|
||||||
|
if (!client || !pagination || !pageInfo) {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
const paginationVariables: Pagination = params ?? {
|
||||||
|
first: pagination.first,
|
||||||
|
after: pageInfo.endCursor,
|
||||||
|
};
|
||||||
|
const res = await client.query<QueryData>({
|
||||||
|
query,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
pagination: paginationVariables,
|
||||||
|
},
|
||||||
|
fetchPolicy,
|
||||||
|
});
|
||||||
|
const insertionData = getData(res.data);
|
||||||
|
const insertionDataPageInfo = pagination.getPageInfo(res.data);
|
||||||
|
({ data, pageInfo } = pagination.append(
|
||||||
|
data,
|
||||||
|
pageInfo,
|
||||||
|
insertionData,
|
||||||
|
insertionDataPageInfo,
|
||||||
|
paginationVariables
|
||||||
|
));
|
||||||
|
totalCount = pagination.getTotalCount(res.data);
|
||||||
|
notifyAll({ insertionData });
|
||||||
|
return insertionData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialFetch = async () => {
|
const initialFetch = async () => {
|
||||||
@ -93,10 +182,16 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
try {
|
try {
|
||||||
const res = await client.query<QueryData>({
|
const res = await client.query<QueryData>({
|
||||||
query,
|
query,
|
||||||
variables,
|
variables: pagination
|
||||||
|
? { ...variables, pagination: { first: pagination.first } }
|
||||||
|
: variables,
|
||||||
fetchPolicy,
|
fetchPolicy,
|
||||||
});
|
});
|
||||||
data = getData(res.data);
|
data = getData(res.data);
|
||||||
|
if (pagination) {
|
||||||
|
pageInfo = pagination.getPageInfo(res.data);
|
||||||
|
totalCount = pagination.getTotalCount(res.data);
|
||||||
|
}
|
||||||
// if there was some updates received from subscription during initial query loading apply them on just received data
|
// if there was some updates received from subscription during initial query loading apply them on just received data
|
||||||
if (data && updateQueue && updateQueue.length > 0) {
|
if (data && updateQueue && updateQueue.length > 0) {
|
||||||
while (updateQueue.length) {
|
while (updateQueue.length) {
|
||||||
@ -165,7 +260,7 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data = newData;
|
data = newData;
|
||||||
notifyAll(delta);
|
notifyAll({ delta });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
() => reload()
|
() => reload()
|
||||||
@ -205,6 +300,7 @@ function makeDataProviderInternal<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
unsubscribe: () => unsubscribe(callback),
|
unsubscribe: () => unsubscribe(callback),
|
||||||
reload,
|
reload,
|
||||||
flush: () => notify(callback),
|
flush: () => notify(callback),
|
||||||
|
load,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -239,7 +335,7 @@ const memoize = <Data, Delta>(
|
|||||||
* @param update Update<Data, Delta> function that will be executed on each onNext, it should update data base on delta, it can reload data provider
|
* @param update Update<Data, Delta> function that will be executed on each onNext, it should update data base on delta, it can reload data provider
|
||||||
* @param getData transforms received query data to format that will be stored in data provider
|
* @param getData transforms received query data to format that will be stored in data provider
|
||||||
* @param getDelta transforms delta data to format that will be stored in data provider
|
* @param getDelta transforms delta data to format that will be stored in data provider
|
||||||
* @param fetchPolicy
|
* @param pagination pagination related functions { getPageInfo, getTotalCount, append, first }
|
||||||
* @returns Subscribe<Data, Delta> subscribe function
|
* @returns Subscribe<Data, Delta> subscribe function
|
||||||
* @example
|
* @example
|
||||||
* const marketMidPriceProvider = makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
* const marketMidPriceProvider = makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
||||||
@ -263,6 +359,12 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
update: Update<Data, Delta>,
|
update: Update<Data, Delta>,
|
||||||
getData: GetData<QueryData, Data>,
|
getData: GetData<QueryData, Data>,
|
||||||
getDelta: GetDelta<SubscriptionData, Delta>,
|
getDelta: GetDelta<SubscriptionData, Delta>,
|
||||||
|
pagination?: {
|
||||||
|
getPageInfo: GetPageInfo<QueryData>;
|
||||||
|
getTotalCount: GetTotalCount<QueryData>;
|
||||||
|
append: Append<Data>;
|
||||||
|
first: number;
|
||||||
|
},
|
||||||
fetchPolicy: FetchPolicy = 'no-cache'
|
fetchPolicy: FetchPolicy = 'no-cache'
|
||||||
): Subscribe<Data, Delta> {
|
): Subscribe<Data, Delta> {
|
||||||
const getInstance = memoize<Data, Delta>(() =>
|
const getInstance = memoize<Data, Delta>(() =>
|
||||||
@ -272,6 +374,7 @@ export function makeDataProvider<QueryData, Data, SubscriptionData, Delta>(
|
|||||||
update,
|
update,
|
||||||
getData,
|
getData,
|
||||||
getDelta,
|
getDelta,
|
||||||
|
pagination,
|
||||||
fetchPolicy
|
fetchPolicy
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import { useCallback, useMemo, useRef } from 'react';
|
|||||||
import {
|
import {
|
||||||
MAX_TRADES,
|
MAX_TRADES,
|
||||||
sortTrades,
|
sortTrades,
|
||||||
tradesDataProvider,
|
tradesDataProvider as dataProvider,
|
||||||
} from './trades-data-provider';
|
} from './trades-data-provider';
|
||||||
import { TradesTable } from './trades-table';
|
import { TradesTable } from './trades-table';
|
||||||
import type { TradeFields } from './__generated__/TradeFields';
|
import type { TradeFields } from './__generated__/TradeFields';
|
||||||
@ -22,7 +22,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
() => ({ marketId, maxTrades: MAX_TRADES }),
|
() => ({ marketId, maxTrades: MAX_TRADES }),
|
||||||
[marketId]
|
[marketId]
|
||||||
);
|
);
|
||||||
const update = useCallback((delta: TradeFields[]) => {
|
const update = useCallback(({ delta }: { delta: TradeFields[] }) => {
|
||||||
if (!gridRef.current?.api) {
|
if (!gridRef.current?.api) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -43,11 +43,11 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, []);
|
}, []);
|
||||||
const { data, error, loading } = useDataProvider(
|
const { data, error, loading } = useDataProvider({
|
||||||
tradesDataProvider,
|
dataProvider,
|
||||||
update,
|
update,
|
||||||
variables
|
variables,
|
||||||
);
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
|
@ -53,8 +53,8 @@ export const sortTrades = (trades: TradeFields[]) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const update = (data: TradeFields[], delta: TradeFields[]) =>
|
const update = (data: TradeFields[], delta: TradeFields[]) => {
|
||||||
produce(data, (draft) => {
|
return produce(data, (draft) => {
|
||||||
const incoming = sortTrades(delta);
|
const incoming = sortTrades(delta);
|
||||||
|
|
||||||
// Add new trades to the top
|
// Add new trades to the top
|
||||||
@ -65,6 +65,7 @@ const update = (data: TradeFields[], delta: TradeFields[]) =>
|
|||||||
draft.splice(MAX_TRADES, draft.length - MAX_TRADES);
|
draft.splice(MAX_TRADES, draft.length - MAX_TRADES);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getData = (responseData: Trades): TradeFields[] | null =>
|
const getData = (responseData: Trades): TradeFields[] | null =>
|
||||||
responseData.market ? responseData.market.trades : null;
|
responseData.market ? responseData.market.trades : null;
|
||||||
|
14
yarn.lock
14
yarn.lock
@ -7437,14 +7437,14 @@ aes-js@^3.1.2:
|
|||||||
integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==
|
integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==
|
||||||
|
|
||||||
ag-grid-community@^27.0.1:
|
ag-grid-community@^27.0.1:
|
||||||
version "27.1.0"
|
version "27.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-27.1.0.tgz#17f73173444a9efc4faea0f0cd6c5090e698f7ee"
|
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-27.3.0.tgz#b1e94a58026aaf2f0cd7920e35833325b5e762c7"
|
||||||
integrity sha512-SWzIJTNa7C6Vinizelcoc1FAJQRt1pDn+A8XHQDO2GTQT+VjBnPL8fg94fLJy0EEvqaN5IhDybNS0nD07SKIQw==
|
integrity sha512-R5oZMXEHXnOLrmhn91J8lR0bv6IAnRcU6maO+wKLMJxffRWaAYFAuw1jt7bdmcKCv8c65F6LEBx4ykSOALa9vA==
|
||||||
|
|
||||||
ag-grid-react@^27.0.1:
|
ag-grid-react@^27.0.1:
|
||||||
version "27.1.0"
|
version "27.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-27.1.0.tgz#3b08203b9731a2b2d5431dddd69d68dc640c311e"
|
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-27.3.0.tgz#fe06647653f8b0b349b8e613aab8ea2e07915562"
|
||||||
integrity sha512-AfRwH6BL/LribvLJ2594Fq0/MfZf/17WebjGj927bM3vABDr2OBX3qgMIaQE+kpV9mABPb51rlWLMmbCvltv2g==
|
integrity sha512-2bs9YfJ/shvBZQLLjny4NFvht+ic6VtpTPO0r3bHHOhlL3Fjx2rGvS6AHSwfvu+kJacHCta30PjaEbX8T3UDyw==
|
||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
@ -17018,7 +17018,7 @@ nx@13.8.1:
|
|||||||
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||||
|
|
||||||
object-copy@^0.1.0:
|
object-copy@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user