Feature/373 pagination of orders (#809)
* feat(#373): switch orders to infinite scroll mode * feat(#373): fix orders tests * feat(#218): improve typing in order-list * feat(#373): add generic getRows for infinite rowModelType
This commit is contained in:
parent
be7690a73e
commit
b88fda787c
@ -70,10 +70,10 @@ describe('orders', () => {
|
||||
|
||||
it('orders are sorted by most recent order', () => {
|
||||
const expectedOrderList = [
|
||||
'UNIDAI.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'TSLA.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
|
||||
cy.getByTestId('tab-orders')
|
||||
|
@ -1,6 +1,9 @@
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { Orders, Orders_party_orders } from '@vegaprotocol/orders';
|
||||
import type {
|
||||
Orders,
|
||||
Orders_party_ordersConnection_edges_node,
|
||||
} from '@vegaprotocol/orders';
|
||||
import {
|
||||
OrderStatus,
|
||||
OrderTimeInForce,
|
||||
@ -9,7 +12,7 @@ import {
|
||||
} from '@vegaprotocol/types';
|
||||
|
||||
export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
const orders: Orders_party_orders[] = [
|
||||
const orders: Orders_party_ordersConnection_edges_node[] = [
|
||||
{
|
||||
__typename: 'Order',
|
||||
id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||
@ -34,7 +37,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
remaining: '0',
|
||||
price: '20000000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2020, 1, 1).toISOString(),
|
||||
createdAt: new Date(2020, 1, 30).toISOString(),
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
@ -63,7 +66,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
remaining: '0',
|
||||
price: '100',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdAt: new Date(2020, 1, 29).toISOString(),
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
@ -92,7 +95,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
remaining: '0',
|
||||
price: '20000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2022, 5, 10).toISOString(),
|
||||
createdAt: new Date(2020, 1, 28).toISOString(),
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
@ -121,17 +124,35 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
remaining: '0',
|
||||
price: '100000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2022, 7, 15).toISOString(),
|
||||
createdAt: new Date(2020, 1, 27).toISOString(),
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultResult = {
|
||||
const defaultResult: Orders = {
|
||||
party: {
|
||||
id: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||
orders,
|
||||
ordersConnection: {
|
||||
__typename: 'OrderConnection',
|
||||
edges: orders.map((f) => {
|
||||
return {
|
||||
__typename: 'OrderEdge',
|
||||
node: f,
|
||||
cursor: f.id,
|
||||
};
|
||||
}),
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
startCursor:
|
||||
'066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||
endCursor:
|
||||
'94737d2bafafa4bc3b80a56ef084ae52a983b91aa067c31e243c61a0f962a836',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
},
|
||||
},
|
||||
__typename: 'Party',
|
||||
},
|
||||
};
|
||||
|
@ -1,13 +1,12 @@
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useRef, useMemo } from 'react';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useDataProvider,
|
||||
makeInfiniteScrollGetRows,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { FillsTable } from './fills-table';
|
||||
import type {
|
||||
IGetRowsParams,
|
||||
BodyScrollEvent,
|
||||
BodyScrollEndEvent,
|
||||
} from 'ag-grid-community';
|
||||
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
|
||||
|
||||
import { fillsDataProvider as dataProvider } from './fills-data-provider';
|
||||
import type { Fills_party_tradesConnection_edges } from './__generated__/Fills';
|
||||
@ -90,36 +89,12 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => {
|
||||
totalCountRef.current = totalCount;
|
||||
dataRef.current = data;
|
||||
|
||||
const getRows = async ({
|
||||
successCallback,
|
||||
failCallback,
|
||||
startRow,
|
||||
endRow,
|
||||
}: IGetRowsParams) => {
|
||||
startRow += newRows.current;
|
||||
endRow += newRows.current;
|
||||
try {
|
||||
if (dataRef.current && dataRef.current.indexOf(null) < endRow) {
|
||||
await load();
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else if (rowsThisBlock.length < endRow - startRow) {
|
||||
lastRow = rowsThisBlock.length;
|
||||
}
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
||||
const getRows = makeInfiniteScrollGetRows<Fills_party_tradesConnection_edges>(
|
||||
newRows,
|
||||
dataRef,
|
||||
totalCountRef,
|
||||
load
|
||||
);
|
||||
|
||||
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
|
@ -3,15 +3,12 @@ import type { Props } from './fills-table';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { makeInfiniteScrollGetRows } from '@vegaprotocol/react-helpers';
|
||||
import { FillsTable } from './fills-table';
|
||||
import { generateFills, generateFill } from './test-helpers';
|
||||
import type { Fills_party_tradesConnection_edges } from './__generated__/Fills';
|
||||
import type { FillsSub_trades } from './__generated__/FillsSub';
|
||||
import type {
|
||||
IGetRowsParams,
|
||||
BodyScrollEvent,
|
||||
BodyScrollEndEvent,
|
||||
} from 'ag-grid-community';
|
||||
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
|
||||
|
||||
export default {
|
||||
component: FillsTable,
|
||||
@ -73,7 +70,7 @@ const useDataProvider = ({
|
||||
const insertionData = getData(start, end);
|
||||
data.splice(start, end - start, ...insertionData);
|
||||
insert({ data, totalCount, insertionData });
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(insertionData);
|
||||
},
|
||||
totalCount,
|
||||
};
|
||||
@ -152,39 +149,12 @@ const PaginationManager = ({ pagination }: PaginationManagerProps) => {
|
||||
totalCountRef.current = totalCount;
|
||||
dataRef.current = data;
|
||||
|
||||
const getRows = async ({
|
||||
successCallback,
|
||||
failCallback,
|
||||
startRow,
|
||||
endRow,
|
||||
}: IGetRowsParams) => {
|
||||
startRow += newRows.current;
|
||||
endRow += newRows.current;
|
||||
try {
|
||||
if (
|
||||
dataRef.current &&
|
||||
dataRef.current.slice(startRow, endRow).some((i) => !i)
|
||||
) {
|
||||
await load(startRow, endRow);
|
||||
}
|
||||
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 {
|
||||
lastRow = totalCountRef.current;
|
||||
}
|
||||
} else if (rowsThisBlock.length < endRow - startRow) {
|
||||
lastRow = rowsThisBlock.length;
|
||||
}
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
||||
const getRows = makeInfiniteScrollGetRows<Fills_party_tradesConnection_edges>(
|
||||
newRows,
|
||||
dataRef,
|
||||
totalCountRef,
|
||||
load
|
||||
);
|
||||
|
||||
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
@ -313,36 +283,12 @@ const InfiniteScrollManager = () => {
|
||||
totalCountRef.current = totalCount;
|
||||
dataRef.current = data;
|
||||
|
||||
const getRows = async ({
|
||||
successCallback,
|
||||
failCallback,
|
||||
startRow,
|
||||
endRow,
|
||||
}: IGetRowsParams) => {
|
||||
startRow += newRows.current;
|
||||
endRow += newRows.current;
|
||||
try {
|
||||
if (dataRef.current && dataRef.current.indexOf(null) < endRow) {
|
||||
await load();
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else if (rowsThisBlock.length < endRow - startRow) {
|
||||
lastRow = rowsThisBlock.length;
|
||||
}
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
||||
const getRows = makeInfiniteScrollGetRows<Fills_party_tradesConnection_edges>(
|
||||
newRows,
|
||||
dataRef,
|
||||
totalCountRef,
|
||||
load
|
||||
);
|
||||
|
||||
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
|
@ -1,121 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL fragment: OrderFields
|
||||
// ====================================================
|
||||
|
||||
export interface OrderFields_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
*/
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface OrderFields_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of or reference to a fully specified instrument.
|
||||
*/
|
||||
instrument: OrderFields_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface OrderFields_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Market full name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||
* number denominated in the currency of the Market. (uint64)
|
||||
*
|
||||
* Examples:
|
||||
* Currency Balance decimalPlaces Real Balance
|
||||
* GBP 100 0 GBP 100
|
||||
* GBP 100 2 GBP 1.00
|
||||
* GBP 100 4 GBP 0.01
|
||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||
*
|
||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
||||
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
||||
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
||||
*/
|
||||
positionDecimalPlaces: number;
|
||||
/**
|
||||
* An instance of or reference to a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: OrderFields_market_tradableInstrument;
|
||||
}
|
||||
|
||||
export interface OrderFields {
|
||||
__typename: "Order";
|
||||
/**
|
||||
* Hash of the order data
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
||||
*/
|
||||
market: OrderFields_market | null;
|
||||
/**
|
||||
* Type the order type (defaults to PARTY)
|
||||
*/
|
||||
type: OrderType | null;
|
||||
/**
|
||||
* Whether the order is to buy or sell
|
||||
*/
|
||||
side: Side;
|
||||
/**
|
||||
* Total number of contracts that may be bought or sold (immutable) (uint64)
|
||||
*/
|
||||
size: string;
|
||||
/**
|
||||
* The status of an order, for example 'Active'
|
||||
*/
|
||||
status: OrderStatus;
|
||||
/**
|
||||
* Reason for the order to be rejected
|
||||
*/
|
||||
rejectionReason: OrderRejectionReason | null;
|
||||
/**
|
||||
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
||||
*/
|
||||
price: string;
|
||||
/**
|
||||
* The timeInForce of order (determines how and if it executes, and whether it persists on the book)
|
||||
*/
|
||||
timeInForce: OrderTimeInForce;
|
||||
/**
|
||||
* Number of contracts remaining of the total that have not yet been bought or sold (uint64)
|
||||
*/
|
||||
remaining: string;
|
||||
/**
|
||||
* Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
|
||||
*/
|
||||
expiresAt: string | null;
|
||||
/**
|
||||
* RFC3339Nano formatted date and time for when the order was created (timestamp)
|
||||
*/
|
||||
createdAt: string;
|
||||
/**
|
||||
* RFC3339Nano time the order was altered
|
||||
*/
|
||||
updatedAt: string | null;
|
||||
}
|
132
libs/orders/src/lib/components/__generated__/OrderSub.ts
generated
132
libs/orders/src/lib/components/__generated__/OrderSub.ts
generated
@ -1,132 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL subscription operation: OrderSub
|
||||
// ====================================================
|
||||
|
||||
export interface OrderSub_orders_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
*/
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface OrderSub_orders_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of or reference to a fully specified instrument.
|
||||
*/
|
||||
instrument: OrderSub_orders_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface OrderSub_orders_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Market full name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||
* number denominated in the currency of the Market. (uint64)
|
||||
*
|
||||
* Examples:
|
||||
* Currency Balance decimalPlaces Real Balance
|
||||
* GBP 100 0 GBP 100
|
||||
* GBP 100 2 GBP 1.00
|
||||
* GBP 100 4 GBP 0.01
|
||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||
*
|
||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
||||
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
||||
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
||||
*/
|
||||
positionDecimalPlaces: number;
|
||||
/**
|
||||
* An instance of or reference to a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: OrderSub_orders_market_tradableInstrument;
|
||||
}
|
||||
|
||||
export interface OrderSub_orders {
|
||||
__typename: "Order";
|
||||
/**
|
||||
* Hash of the order data
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
||||
*/
|
||||
market: OrderSub_orders_market | null;
|
||||
/**
|
||||
* Type the order type (defaults to PARTY)
|
||||
*/
|
||||
type: OrderType | null;
|
||||
/**
|
||||
* Whether the order is to buy or sell
|
||||
*/
|
||||
side: Side;
|
||||
/**
|
||||
* Total number of contracts that may be bought or sold (immutable) (uint64)
|
||||
*/
|
||||
size: string;
|
||||
/**
|
||||
* The status of an order, for example 'Active'
|
||||
*/
|
||||
status: OrderStatus;
|
||||
/**
|
||||
* Reason for the order to be rejected
|
||||
*/
|
||||
rejectionReason: OrderRejectionReason | null;
|
||||
/**
|
||||
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
||||
*/
|
||||
price: string;
|
||||
/**
|
||||
* The timeInForce of order (determines how and if it executes, and whether it persists on the book)
|
||||
*/
|
||||
timeInForce: OrderTimeInForce;
|
||||
/**
|
||||
* Number of contracts remaining of the total that have not yet been bought or sold (uint64)
|
||||
*/
|
||||
remaining: string;
|
||||
/**
|
||||
* Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
|
||||
*/
|
||||
expiresAt: string | null;
|
||||
/**
|
||||
* RFC3339Nano formatted date and time for when the order was created (timestamp)
|
||||
*/
|
||||
createdAt: string;
|
||||
/**
|
||||
* RFC3339Nano time the order was altered
|
||||
*/
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface OrderSub {
|
||||
/**
|
||||
* Subscribe to orders updates
|
||||
*/
|
||||
orders: OrderSub_orders[] | null;
|
||||
}
|
||||
|
||||
export interface OrderSubVariables {
|
||||
partyId: string;
|
||||
}
|
144
libs/orders/src/lib/components/__generated__/Orders.ts
generated
144
libs/orders/src/lib/components/__generated__/Orders.ts
generated
@ -1,144 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: Orders
|
||||
// ====================================================
|
||||
|
||||
export interface Orders_party_orders_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
*/
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of or reference to a fully specified instrument.
|
||||
*/
|
||||
instrument: Orders_party_orders_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Market full name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||
* number denominated in the currency of the Market. (uint64)
|
||||
*
|
||||
* Examples:
|
||||
* Currency Balance decimalPlaces Real Balance
|
||||
* GBP 100 0 GBP 100
|
||||
* GBP 100 2 GBP 1.00
|
||||
* GBP 100 4 GBP 0.01
|
||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||
*
|
||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
||||
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
||||
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
||||
*/
|
||||
positionDecimalPlaces: number;
|
||||
/**
|
||||
* An instance of or reference to a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: Orders_party_orders_market_tradableInstrument;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders {
|
||||
__typename: "Order";
|
||||
/**
|
||||
* Hash of the order data
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
||||
*/
|
||||
market: Orders_party_orders_market | null;
|
||||
/**
|
||||
* Type the order type (defaults to PARTY)
|
||||
*/
|
||||
type: OrderType | null;
|
||||
/**
|
||||
* Whether the order is to buy or sell
|
||||
*/
|
||||
side: Side;
|
||||
/**
|
||||
* Total number of contracts that may be bought or sold (immutable) (uint64)
|
||||
*/
|
||||
size: string;
|
||||
/**
|
||||
* The status of an order, for example 'Active'
|
||||
*/
|
||||
status: OrderStatus;
|
||||
/**
|
||||
* Reason for the order to be rejected
|
||||
*/
|
||||
rejectionReason: OrderRejectionReason | null;
|
||||
/**
|
||||
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
||||
*/
|
||||
price: string;
|
||||
/**
|
||||
* The timeInForce of order (determines how and if it executes, and whether it persists on the book)
|
||||
*/
|
||||
timeInForce: OrderTimeInForce;
|
||||
/**
|
||||
* Number of contracts remaining of the total that have not yet been bought or sold (uint64)
|
||||
*/
|
||||
remaining: string;
|
||||
/**
|
||||
* Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
|
||||
*/
|
||||
expiresAt: string | null;
|
||||
/**
|
||||
* RFC3339Nano formatted date and time for when the order was created (timestamp)
|
||||
*/
|
||||
createdAt: string;
|
||||
/**
|
||||
* RFC3339Nano time the order was altered
|
||||
*/
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface Orders_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Orders relating to a party
|
||||
*/
|
||||
orders: Orders_party_orders[] | null;
|
||||
}
|
||||
|
||||
export interface Orders {
|
||||
/**
|
||||
* An entity that is trading on the VEGA network
|
||||
*/
|
||||
party: Orders_party | null;
|
||||
}
|
||||
|
||||
export interface OrdersVariables {
|
||||
partyId: string;
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
export * from './__generated__';
|
||||
export * from './mocks';
|
||||
export * from './order-data-provider';
|
||||
export * from './order-list';
|
||||
export * from './order-list-manager';
|
||||
|
@ -1,28 +1,15 @@
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
import {
|
||||
OrderStatus,
|
||||
OrderTimeInForce,
|
||||
OrderType,
|
||||
Side,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { Orders, Orders_party_orders } from '../__generated__/Orders';
|
||||
import type { Orders_party_ordersConnection_edges_node } from '../';
|
||||
|
||||
export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
||||
const orders: Orders_party_orders[] = generateOrdersArray();
|
||||
|
||||
const defaultResult = {
|
||||
party: {
|
||||
id: 'party-id',
|
||||
orders,
|
||||
__typename: 'Party',
|
||||
},
|
||||
};
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
|
||||
export const generateOrder = (partialOrder?: Partial<Orders_party_orders>) =>
|
||||
export const generateOrder = (
|
||||
partialOrder: Partial<Orders_party_ordersConnection_edges_node>
|
||||
) =>
|
||||
merge(
|
||||
{
|
||||
__typename: 'Order',
|
||||
@ -52,7 +39,7 @@ export const generateOrder = (partialOrder?: Partial<Orders_party_orders>) =>
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
} as Orders_party_orders,
|
||||
} as Orders_party_ordersConnection_edges_node,
|
||||
partialOrder
|
||||
);
|
||||
|
||||
@ -71,86 +58,88 @@ export const marketOrder = generateOrder({
|
||||
status: OrderStatus.Active,
|
||||
});
|
||||
|
||||
export const generateMockOrders = (): Orders_party_orders[] => {
|
||||
return [
|
||||
generateOrder({
|
||||
id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'AAVEDAI.MF21',
|
||||
export const generateMockOrders =
|
||||
(): Orders_party_ordersConnection_edges_node[] => {
|
||||
return [
|
||||
generateOrder({
|
||||
id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
|
||||
name: 'AAVEDAI Monthly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'AAVEDAI.MF21',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
size: '10',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '20000000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2020, 1, 1).toISOString(),
|
||||
}),
|
||||
generateOrder({
|
||||
id: '48DB6767E4E4E0F649C5A13ABFADE39F8451C27DA828DAF14B7A1E8E5EBDAD99',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||
name: 'Tesla Quarterly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'TSLA.QM21',
|
||||
size: '10',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '20000000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2020, 1, 1).toISOString(),
|
||||
}),
|
||||
generateOrder({
|
||||
id: '48DB6767E4E4E0F649C5A13ABFADE39F8451C27DA828DAF14B7A1E8E5EBDAD99',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
|
||||
name: 'Tesla Quarterly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'TSLA.QM21',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
size: '1',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '100',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
generateOrder({
|
||||
id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'BTCUSD.MF21',
|
||||
size: '1',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '100',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
generateOrder({
|
||||
id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
|
||||
name: 'BTCUSD Monthly (30 Jun 2022)',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'BTCUSD.MF21',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
size: '1',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '20000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2022, 5, 10).toISOString(),
|
||||
}),
|
||||
];
|
||||
};
|
||||
size: '1',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Filled,
|
||||
side: Side.Buy,
|
||||
remaining: '0',
|
||||
price: '20000',
|
||||
timeInForce: OrderTimeInForce.GTC,
|
||||
createdAt: new Date(2022, 5, 10).toISOString(),
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
||||
export const generateOrdersArray = (): Orders_party_orders[] => {
|
||||
return [marketOrder, limitOrder, ...generateMockOrders()];
|
||||
};
|
||||
export const generateOrdersArray =
|
||||
(): Orders_party_ordersConnection_edges_node[] => {
|
||||
return [marketOrder, limitOrder, ...generateMockOrders()];
|
||||
};
|
||||
|
@ -3,13 +3,13 @@
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
|
||||
import { Pagination, OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: Orders
|
||||
// ====================================================
|
||||
|
||||
export interface Orders_party_orders_market_tradableInstrument_instrument {
|
||||
export interface Orders_party_ordersConnection_edges_node_market_tradableInstrument_instrument {
|
||||
__typename: "Instrument";
|
||||
/**
|
||||
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
|
||||
@ -17,15 +17,15 @@ export interface Orders_party_orders_market_tradableInstrument_instrument {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders_market_tradableInstrument {
|
||||
export interface Orders_party_ordersConnection_edges_node_market_tradableInstrument {
|
||||
__typename: "TradableInstrument";
|
||||
/**
|
||||
* An instance of or reference to a fully specified instrument.
|
||||
*/
|
||||
instrument: Orders_party_orders_market_tradableInstrument_instrument;
|
||||
instrument: Orders_party_ordersConnection_edges_node_market_tradableInstrument_instrument;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders_market {
|
||||
export interface Orders_party_ordersConnection_edges_node_market {
|
||||
__typename: "Market";
|
||||
/**
|
||||
* Market ID
|
||||
@ -61,10 +61,10 @@ export interface Orders_party_orders_market {
|
||||
/**
|
||||
* An instance of or reference to a tradable instrument.
|
||||
*/
|
||||
tradableInstrument: Orders_party_orders_market_tradableInstrument;
|
||||
tradableInstrument: Orders_party_ordersConnection_edges_node_market_tradableInstrument;
|
||||
}
|
||||
|
||||
export interface Orders_party_orders {
|
||||
export interface Orders_party_ordersConnection_edges_node {
|
||||
__typename: "Order";
|
||||
/**
|
||||
* Hash of the order data
|
||||
@ -73,7 +73,7 @@ export interface Orders_party_orders {
|
||||
/**
|
||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
||||
*/
|
||||
market: Orders_party_orders_market | null;
|
||||
market: Orders_party_ordersConnection_edges_node_market | null;
|
||||
/**
|
||||
* Type the order type (defaults to PARTY)
|
||||
*/
|
||||
@ -120,6 +120,32 @@ export interface Orders_party_orders {
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export interface Orders_party_ordersConnection_edges {
|
||||
__typename: "OrderEdge";
|
||||
node: Orders_party_ordersConnection_edges_node;
|
||||
cursor: string | null;
|
||||
}
|
||||
|
||||
export interface Orders_party_ordersConnection_pageInfo {
|
||||
__typename: "PageInfo";
|
||||
startCursor: string;
|
||||
endCursor: string;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
}
|
||||
|
||||
export interface Orders_party_ordersConnection {
|
||||
__typename: "OrderConnection";
|
||||
/**
|
||||
* The orders in this connection
|
||||
*/
|
||||
edges: Orders_party_ordersConnection_edges[] | null;
|
||||
/**
|
||||
* The pagination information
|
||||
*/
|
||||
pageInfo: Orders_party_ordersConnection_pageInfo | null;
|
||||
}
|
||||
|
||||
export interface Orders_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
@ -129,7 +155,7 @@ export interface Orders_party {
|
||||
/**
|
||||
* Orders relating to a party
|
||||
*/
|
||||
orders: Orders_party_orders[] | null;
|
||||
ordersConnection: Orders_party_ordersConnection;
|
||||
}
|
||||
|
||||
export interface Orders {
|
||||
@ -141,4 +167,5 @@ export interface Orders {
|
||||
|
||||
export interface OrdersVariables {
|
||||
partyId: string;
|
||||
pagination?: Pagination | null;
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './__generated__';
|
||||
export * from './order-data-provider';
|
||||
|
@ -1,88 +1,59 @@
|
||||
import {
|
||||
OrderType,
|
||||
OrderStatus,
|
||||
Side,
|
||||
OrderTimeInForce,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { Orders_party_orders } from '../__generated__/Orders';
|
||||
import { sortOrders } from './order-data-provider';
|
||||
|
||||
const marketOrder: Orders_party_orders = {
|
||||
__typename: 'Order',
|
||||
id: 'order-id',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'market-id',
|
||||
name: 'market-name',
|
||||
decimalPlaces: 2,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'instrument-code',
|
||||
import { update } from './order-data-provider';
|
||||
import type { OrderSub_orders, Orders_party_ordersConnection_edges } from '../';
|
||||
describe('order data provider', () => {
|
||||
it('puts incoming data in proper place', () => {
|
||||
const data = [
|
||||
{
|
||||
node: {
|
||||
id: '1',
|
||||
updatedAt: new Date('2022-01-31').toISOString(),
|
||||
createdAt: new Date('2022-01-29').toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
size: '10',
|
||||
type: OrderType.Market,
|
||||
status: OrderStatus.Active,
|
||||
side: Side.Buy,
|
||||
remaining: '5',
|
||||
price: '',
|
||||
timeInForce: OrderTimeInForce.IOC,
|
||||
createdAt: new Date('2022-2-3').toISOString(),
|
||||
updatedAt: null,
|
||||
expiresAt: null,
|
||||
rejectionReason: null,
|
||||
};
|
||||
|
||||
const limitOrder: Orders_party_orders = {
|
||||
__typename: 'Order',
|
||||
id: 'order-id',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'market-id',
|
||||
name: 'market-name',
|
||||
decimalPlaces: 2,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
code: 'instrument-code',
|
||||
{
|
||||
node: {
|
||||
id: '2',
|
||||
createdAt: new Date('2022-01-30').toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
size: '10',
|
||||
type: OrderType.Limit,
|
||||
status: OrderStatus.Active,
|
||||
side: Side.Sell,
|
||||
remaining: '5',
|
||||
price: '12345',
|
||||
timeInForce: OrderTimeInForce.GTT,
|
||||
createdAt: new Date('2022-3-3').toISOString(),
|
||||
expiresAt: new Date('2022-3-5').toISOString(),
|
||||
updatedAt: null,
|
||||
rejectionReason: null,
|
||||
};
|
||||
] as Orders_party_ordersConnection_edges[];
|
||||
|
||||
describe('OrderDataProvider', () => {
|
||||
const orders = [marketOrder, limitOrder];
|
||||
const delta = [
|
||||
// this one should be dropped because id don't exits and it's older than newest
|
||||
{
|
||||
id: '0',
|
||||
createdAt: new Date('2022-01-30').toISOString(),
|
||||
},
|
||||
// this one should be dropped because new newer below
|
||||
{
|
||||
id: '1',
|
||||
updatedAt: new Date('2022-02-01').toISOString(),
|
||||
createdAt: new Date('2022-01-29').toISOString(),
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
updatedAt: new Date('2022-02-02').toISOString(),
|
||||
createdAt: new Date('2022-01-29').toISOString(),
|
||||
},
|
||||
// this should be added
|
||||
{
|
||||
id: '4',
|
||||
createdAt: new Date('2022-02-04').toISOString(),
|
||||
},
|
||||
// this should be move to top
|
||||
{
|
||||
id: '2',
|
||||
updatedAt: new Date('2022-02-03').toISOString(),
|
||||
createdAt: new Date('2022-01-29').toISOString(),
|
||||
},
|
||||
] as OrderSub_orders[];
|
||||
|
||||
describe('sortOrders', () => {
|
||||
it('should sort the orders from the most recent placed to the oldest', () => {
|
||||
expect(sortOrders(orders)).toStrictEqual([limitOrder, marketOrder]);
|
||||
});
|
||||
|
||||
it('should sort the orders from the most recent updated to the oldest', () => {
|
||||
const updatedOrder = {
|
||||
...limitOrder,
|
||||
updatedAt: new Date('2022-3-4').toISOString(),
|
||||
};
|
||||
expect(sortOrders([...orders, updatedOrder])).toStrictEqual([
|
||||
updatedOrder,
|
||||
limitOrder,
|
||||
marketOrder,
|
||||
]);
|
||||
});
|
||||
const updatedData = update(data, delta);
|
||||
expect(updatedData.findIndex((edge) => edge.node === delta[0])).toEqual(-1);
|
||||
expect(updatedData[2].node.id).toEqual(delta[2].id);
|
||||
expect(updatedData[2].node.updatedAt).toEqual(delta[2].updatedAt);
|
||||
expect(updatedData[0].node).toEqual(delta[3]);
|
||||
expect(updatedData[1].node.id).toEqual(delta[4].id);
|
||||
expect(updatedData[1].node.updatedAt).toEqual(delta[4].updatedAt);
|
||||
});
|
||||
});
|
||||
|
@ -1,11 +1,18 @@
|
||||
import produce from 'immer';
|
||||
import { gql } from '@apollo/client';
|
||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import type { OrderFields } from '../__generated__/OrderFields';
|
||||
import type { Orders, Orders_party_orders } from '../__generated__/Orders';
|
||||
import type { OrderSub } from '../__generated__/OrderSub';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import {
|
||||
makeDataProvider,
|
||||
defaultAppend as append,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type { PageInfo } from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
Orders,
|
||||
Orders_party_ordersConnection_edges,
|
||||
OrderSub,
|
||||
OrderFields,
|
||||
} from '../';
|
||||
|
||||
const ORDER_FRAGMENT = gql`
|
||||
fragment OrderFields on Order {
|
||||
@ -37,11 +44,22 @@ const ORDER_FRAGMENT = gql`
|
||||
|
||||
export const ORDERS_QUERY = gql`
|
||||
${ORDER_FRAGMENT}
|
||||
query Orders($partyId: ID!) {
|
||||
query Orders($partyId: ID!, $pagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
orders {
|
||||
...OrderFields
|
||||
ordersConnection(pagination: $pagination) {
|
||||
edges {
|
||||
node {
|
||||
...OrderFields
|
||||
}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
startCursor
|
||||
endCursor
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,53 +74,55 @@ export const ORDERS_SUB = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
// A single update can contain the same order with multiple updates, so we need to find
|
||||
// the latest version of the order and only update using that
|
||||
export const sortOrders = (orders: OrderFields[]) => {
|
||||
return orderBy(
|
||||
orders,
|
||||
(o) => {
|
||||
if (!o.updatedAt) return new Date(o.createdAt).getTime();
|
||||
return new Date(o.updatedAt).getTime();
|
||||
},
|
||||
'desc'
|
||||
);
|
||||
};
|
||||
|
||||
export const uniqOrders = (orders: OrderFields[]) => {
|
||||
return uniqBy(orders, 'id');
|
||||
};
|
||||
|
||||
export const prepareIncomingOrders = (delta: OrderFields[]) => {
|
||||
const sortedOrders = sortOrders(delta);
|
||||
const incoming = uniqOrders(sortedOrders);
|
||||
return incoming;
|
||||
};
|
||||
|
||||
const update = (data: OrderFields[], delta: OrderFields[]) => {
|
||||
export const update = (
|
||||
data: Orders_party_ordersConnection_edges[],
|
||||
delta: OrderFields[]
|
||||
) => {
|
||||
return produce(data, (draft) => {
|
||||
const incoming = prepareIncomingOrders(delta);
|
||||
// A single update can contain the same order with multiple updates, so we need to find
|
||||
// the latest version of the order and only update using that
|
||||
const incoming = uniqBy(
|
||||
orderBy(delta, (order) => order.updatedAt || order.createdAt, 'desc'),
|
||||
'id'
|
||||
);
|
||||
|
||||
// Add or update incoming orders
|
||||
incoming.forEach((order) => {
|
||||
const index = draft.findIndex((o) => o.id === order.id);
|
||||
if (index === -1) {
|
||||
draft.unshift(order);
|
||||
} else {
|
||||
draft[index] = order;
|
||||
incoming.reverse().forEach((node) => {
|
||||
const index = draft.findIndex((edge) => edge.node.id === node.id);
|
||||
const newer =
|
||||
(node.updatedAt || node.createdAt) >=
|
||||
(draft[0].node.updatedAt || draft[0].node.createdAt);
|
||||
if (index !== -1) {
|
||||
Object.assign(draft[index].node, node);
|
||||
if (newer) {
|
||||
draft.unshift(...draft.splice(index, 1));
|
||||
}
|
||||
} else if (newer) {
|
||||
draft.unshift({ node, cursor: '', __typename: 'OrderEdge' });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getData = (responseData: Orders): Orders_party_orders[] | null =>
|
||||
responseData?.party?.orders || null;
|
||||
const getData = (
|
||||
responseData: Orders
|
||||
): Orders_party_ordersConnection_edges[] | null =>
|
||||
responseData?.party?.ordersConnection.edges || null;
|
||||
|
||||
const getDelta = (subscriptionData: OrderSub) => subscriptionData.orders || [];
|
||||
|
||||
const getPageInfo = (responseData: Orders): PageInfo | null =>
|
||||
responseData.party?.ordersConnection.pageInfo || null;
|
||||
|
||||
export const ordersDataProvider = makeDataProvider(
|
||||
ORDERS_QUERY,
|
||||
ORDERS_SUB,
|
||||
update,
|
||||
getData,
|
||||
getDelta
|
||||
getDelta,
|
||||
{
|
||||
getPageInfo,
|
||||
append,
|
||||
first: 100,
|
||||
}
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { OrderListManager } from './order-list-manager';
|
||||
import * as useDataProviderHook from '@vegaprotocol/react-helpers';
|
||||
import type { Orders_party_orders } from '../__generated__/Orders';
|
||||
import * as orderListMock from '../order-list';
|
||||
import type { Orders_party_ordersConnection_edges_node } from '../';
|
||||
import * as orderListMock from '../';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
jest.mock('./order-list');
|
||||
@ -12,6 +12,10 @@ it('Renders a loading state while awaiting orders', () => {
|
||||
data: [],
|
||||
loading: true,
|
||||
error: undefined,
|
||||
flush: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
load: jest.fn(),
|
||||
totalCount: 0,
|
||||
});
|
||||
render(<OrderListManager partyId="0x123" />);
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
@ -23,6 +27,10 @@ it('Renders an error state', () => {
|
||||
data: [],
|
||||
loading: false,
|
||||
error: new Error(errorMsg),
|
||||
flush: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
load: jest.fn(),
|
||||
totalCount: undefined,
|
||||
});
|
||||
render(<OrderListManager partyId="0x123" />);
|
||||
expect(
|
||||
@ -35,9 +43,13 @@ it('Renders the order list if orders provided', async () => {
|
||||
// avoid warnings about padding refs
|
||||
orderListMock.OrderList = forwardRef(() => <div>OrderList</div>);
|
||||
jest.spyOn(useDataProviderHook, 'useDataProvider').mockReturnValue({
|
||||
data: [{ id: '1' } as Orders_party_orders],
|
||||
data: [{ id: '1' } as Orders_party_ordersConnection_edges_node],
|
||||
loading: false,
|
||||
error: undefined,
|
||||
flush: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
load: jest.fn(),
|
||||
totalCount: undefined,
|
||||
});
|
||||
render(<OrderListManager partyId="0x123" />);
|
||||
expect(await screen.findByText('OrderList')).toBeInTheDocument();
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { OrderList } from '../order-list';
|
||||
import type { OrderFields } from '../__generated__/OrderFields';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
ordersDataProvider as dataProvider,
|
||||
prepareIncomingOrders,
|
||||
sortOrders,
|
||||
} from '../order-data-provider';
|
||||
useDataProvider,
|
||||
makeInfiniteScrollGetRows,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { OrderSub_orders } from '../__generated__/OrderSub';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import { OrderList, ordersDataProvider as dataProvider } from '../';
|
||||
import type { OrderFields, Orders_party_ordersConnection_edges } from '../';
|
||||
|
||||
interface OrderListManagerProps {
|
||||
partyId: string;
|
||||
@ -18,62 +16,105 @@ interface OrderListManagerProps {
|
||||
|
||||
export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const dataRef = useRef<(Orders_party_ordersConnection_edges | null)[] | null>(
|
||||
null
|
||||
);
|
||||
const totalCountRef = useRef<number | undefined>(undefined);
|
||||
const newRows = useRef(0);
|
||||
const scrolledToTop = useRef(true);
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
|
||||
// Apply updates to the table
|
||||
const update = useCallback(({ delta }: { delta: OrderSub_orders[] }) => {
|
||||
if (!gridRef.current) {
|
||||
return false;
|
||||
const addNewRows = useCallback(() => {
|
||||
if (newRows.current === 0) {
|
||||
return;
|
||||
}
|
||||
const incoming = prepareIncomingOrders(delta);
|
||||
|
||||
const updateRows: OrderFields[] = [];
|
||||
const add: OrderFields[] = [];
|
||||
|
||||
incoming.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,
|
||||
});
|
||||
if (totalCountRef.current !== undefined) {
|
||||
totalCountRef.current += newRows.current;
|
||||
}
|
||||
|
||||
return true;
|
||||
newRows.current = 0;
|
||||
if (!gridRef.current?.api) {
|
||||
return;
|
||||
}
|
||||
gridRef.current.api.refreshInfiniteCache();
|
||||
}, []);
|
||||
|
||||
const { data, error, loading } = useDataProvider({
|
||||
const update = useCallback(
|
||||
({
|
||||
data,
|
||||
delta,
|
||||
}: {
|
||||
data: (Orders_party_ordersConnection_edges | null)[];
|
||||
delta: OrderFields[];
|
||||
}) => {
|
||||
if (!gridRef.current?.api) {
|
||||
return false;
|
||||
}
|
||||
if (!scrolledToTop.current) {
|
||||
const createdAt = dataRef.current?.[0]?.node.createdAt;
|
||||
if (createdAt) {
|
||||
newRows.current += delta.filter(
|
||||
(trade) => trade.createdAt > createdAt
|
||||
).length;
|
||||
}
|
||||
}
|
||||
dataRef.current = data;
|
||||
gridRef.current.api.refreshInfiniteCache();
|
||||
return true;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const insert = useCallback(
|
||||
({
|
||||
data,
|
||||
totalCount,
|
||||
}: {
|
||||
data: Orders_party_ordersConnection_edges[];
|
||||
totalCount?: number;
|
||||
}) => {
|
||||
dataRef.current = data;
|
||||
totalCountRef.current = totalCount;
|
||||
return true;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const { data, error, loading, load, totalCount } = useDataProvider({
|
||||
dataProvider,
|
||||
update,
|
||||
insert,
|
||||
variables,
|
||||
});
|
||||
totalCountRef.current = totalCount;
|
||||
dataRef.current = data;
|
||||
|
||||
const orders = useMemo(() => {
|
||||
if (!data) {
|
||||
return null;
|
||||
const getRows =
|
||||
makeInfiniteScrollGetRows<Orders_party_ordersConnection_edges>(
|
||||
newRows,
|
||||
dataRef,
|
||||
totalCountRef,
|
||||
load
|
||||
);
|
||||
|
||||
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
addNewRows();
|
||||
}
|
||||
return sortOrders(data);
|
||||
}, [data]);
|
||||
};
|
||||
|
||||
const onBodyScroll = (event: BodyScrollEvent) => {
|
||||
scrolledToTop.current = event.top <= 0;
|
||||
};
|
||||
|
||||
// We can set <OrderList showCancelled={false} to hide cancelled orders
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={orders}>
|
||||
<OrderList ref={gridRef} data={orders} />
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<OrderList
|
||||
ref={gridRef}
|
||||
rowModelType="infinite"
|
||||
datasource={{ getRows }}
|
||||
onBodyScrollEnd={onBodyScrollEnd}
|
||||
onBodyScroll={onBodyScroll}
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
@ -4,24 +4,25 @@ import {
|
||||
formatLabel,
|
||||
getDateTimeFormat,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type { Orders_party_orders } from '../__generated__/Orders';
|
||||
import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
|
||||
import { OrderListTable } from './order-list';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
import { OrderListTable } from '../';
|
||||
import type { Orders_party_ordersConnection_edges_node } from '../';
|
||||
import { limitOrder, marketOrder } from '../mocks/generate-orders';
|
||||
|
||||
const generateJsx = (
|
||||
orders: Orders_party_orders[] | null,
|
||||
orders: Orders_party_ordersConnection_edges_node[] | null,
|
||||
context: PartialDeep<VegaWalletContextShape> = { keypair: { pub: '0x123' } }
|
||||
) => {
|
||||
return (
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<OrderListTable
|
||||
data={orders}
|
||||
rowData={orders}
|
||||
cancel={jest.fn()}
|
||||
setEditOrderDialogOpen={jest.fn()}
|
||||
setEditOrder={jest.fn()}
|
||||
|
@ -16,7 +16,7 @@ const Template: Story = (args) => {
|
||||
return (
|
||||
<div style={{ height: 1000 }}>
|
||||
<OrderListTable
|
||||
data={args.data}
|
||||
rowData={args.data}
|
||||
cancel={cancel}
|
||||
setEditOrderDialogOpen={() => {
|
||||
return;
|
||||
@ -55,7 +55,7 @@ const Template2: Story = (args) => {
|
||||
<>
|
||||
<div style={{ height: 1000 }}>
|
||||
<OrderListTable
|
||||
data={args.data}
|
||||
rowData={args.data}
|
||||
cancel={cancel}
|
||||
setEditOrderDialogOpen={() => {
|
||||
return;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types';
|
||||
import type { Orders_party_orders } from '../__generated__/Orders';
|
||||
import {
|
||||
addDecimal,
|
||||
formatLabel,
|
||||
@ -11,27 +10,29 @@ import type {
|
||||
ICellRendererParams,
|
||||
ValueFormatterParams,
|
||||
} from 'ag-grid-community';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type {
|
||||
AgGridReact,
|
||||
AgGridReactProps,
|
||||
AgReactUiProps,
|
||||
} from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import type { Orders_party_ordersConnection_edges_node } from '../';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { useOrderCancel } from '../../order-hooks/use-order-cancel';
|
||||
import { VegaTransactionDialog } from '@vegaprotocol/wallet';
|
||||
import { useOrderEdit } from '../../order-hooks/use-order-edit';
|
||||
import { OrderEditDialog } from './order-edit-dialog';
|
||||
|
||||
interface OrderListProps {
|
||||
data: Orders_party_orders[] | null;
|
||||
showCancelled?: boolean;
|
||||
}
|
||||
type OrderListProps = AgGridReactProps | AgReactUiProps;
|
||||
|
||||
export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||
({ data, showCancelled = true }, ref) => {
|
||||
(props, ref) => {
|
||||
const [cancelOrderDialogOpen, setCancelOrderDialogOpen] = useState(false);
|
||||
const [editOrderDialogOpen, setEditOrderDialogOpen] = useState(false);
|
||||
const [editOrder, setEditOrder] = useState<Orders_party_orders | null>(
|
||||
null
|
||||
);
|
||||
const [editOrder, setEditOrder] =
|
||||
useState<Orders_party_ordersConnection_edges_node | null>(null);
|
||||
|
||||
const { transaction, updatedOrder, reset, cancel } = useOrderCancel();
|
||||
const {
|
||||
@ -40,9 +41,6 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||
reset: resetEdit,
|
||||
edit,
|
||||
} = useOrderEdit();
|
||||
const ordersData = showCancelled
|
||||
? data
|
||||
: data?.filter((o) => o.status !== OrderStatus.Cancelled) || null;
|
||||
const getCancelDialogTitle = (status?: string) => {
|
||||
switch (status) {
|
||||
case OrderStatus.Cancelled:
|
||||
@ -70,7 +68,7 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||
return (
|
||||
<>
|
||||
<OrderListTable
|
||||
data={ordersData}
|
||||
{...props}
|
||||
cancel={cancel}
|
||||
ref={ref}
|
||||
setEditOrderDialogOpen={setEditOrderDialogOpen}
|
||||
@ -105,24 +103,32 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||
}
|
||||
);
|
||||
|
||||
interface OrderListTableProps {
|
||||
data: Orders_party_orders[] | null;
|
||||
type OrderListTableValueFormatterParams = Omit<
|
||||
ValueFormatterParams,
|
||||
'data' | 'value'
|
||||
> & {
|
||||
data: Orders_party_ordersConnection_edges_node | null;
|
||||
};
|
||||
|
||||
type OrderListTableProps = (AgGridReactProps | AgReactUiProps) & {
|
||||
cancel: (body?: unknown) => Promise<unknown>;
|
||||
setEditOrderDialogOpen: (value: boolean) => void;
|
||||
setEditOrder: (order: Orders_party_orders | null) => void;
|
||||
}
|
||||
setEditOrder: (
|
||||
order: Orders_party_ordersConnection_edges_node | null
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
({ data, cancel, setEditOrderDialogOpen, setEditOrder }, ref) => {
|
||||
({ cancel, setEditOrderDialogOpen, setEditOrder, ...props }, ref) => {
|
||||
return (
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate="No orders"
|
||||
defaultColDef={{ flex: 1, resizable: true }}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
getRowId={({ data }) => data.id}
|
||||
rowHeight={40}
|
||||
{...props}
|
||||
>
|
||||
<AgGridColumn
|
||||
headerName={t('Market')}
|
||||
@ -132,7 +138,15 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
headerName={t('Amount')}
|
||||
field="size"
|
||||
cellClass="font-mono"
|
||||
valueFormatter={({ value, data }: ValueFormatterParams) => {
|
||||
valueFormatter={({
|
||||
value,
|
||||
data,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['size'];
|
||||
}) => {
|
||||
if (value === undefined || !data || !data.market) {
|
||||
return undefined;
|
||||
}
|
||||
const prefix = data.side === Side.Buy ? '+' : '-';
|
||||
return (
|
||||
prefix + addDecimal(value, data.market.positionDecimalPlaces)
|
||||
@ -142,11 +156,20 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
<AgGridColumn field="type" />
|
||||
<AgGridColumn
|
||||
field="status"
|
||||
valueFormatter={({ value, data }: ValueFormatterParams) => {
|
||||
if (value === OrderStatus.Rejected) {
|
||||
return `${value}: ${formatLabel(data.rejectionReason)}`;
|
||||
valueFormatter={({
|
||||
value,
|
||||
data,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['status'];
|
||||
}) => {
|
||||
if (value === undefined || !data || !data.market) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === OrderStatus.Rejected) {
|
||||
return `${value}: ${
|
||||
data.rejectionReason && formatLabel(data.rejectionReason)
|
||||
}`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}}
|
||||
/>
|
||||
@ -154,10 +177,18 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
headerName={t('Filled')}
|
||||
field="remaining"
|
||||
cellClass="font-mono"
|
||||
valueFormatter={({ data }: ValueFormatterParams) => {
|
||||
valueFormatter={({
|
||||
data,
|
||||
value,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['remaining'];
|
||||
}) => {
|
||||
if (value === undefined || !data || !data.market) {
|
||||
return undefined;
|
||||
}
|
||||
const dps = data.market.positionDecimalPlaces;
|
||||
const size = new BigNumber(data.size);
|
||||
const remaining = new BigNumber(data.remaining);
|
||||
const remaining = new BigNumber(value);
|
||||
const fills = size.minus(remaining);
|
||||
return `${addDecimal(fills.toString(), dps)}/${addDecimal(
|
||||
size.toString(),
|
||||
@ -168,8 +199,18 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
<AgGridColumn
|
||||
field="price"
|
||||
cellClass="font-mono"
|
||||
valueFormatter={({ value, data }: ValueFormatterParams) => {
|
||||
if (data.type === 'Market') {
|
||||
valueFormatter={({
|
||||
value,
|
||||
data,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['price'];
|
||||
}) => {
|
||||
if (
|
||||
value === undefined ||
|
||||
!data ||
|
||||
!data.market ||
|
||||
data.type === 'Market'
|
||||
) {
|
||||
return '-';
|
||||
}
|
||||
return addDecimal(value, data.market.decimalPlaces);
|
||||
@ -177,7 +218,15 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
/>
|
||||
<AgGridColumn
|
||||
field="timeInForce"
|
||||
valueFormatter={({ value, data }: ValueFormatterParams) => {
|
||||
valueFormatter={({
|
||||
value,
|
||||
data,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['timeInForce'];
|
||||
}) => {
|
||||
if (value === undefined || !data || !data.market) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === OrderTimeInForce.GTT && data.expiresAt) {
|
||||
const expiry = getDateTimeFormat().format(
|
||||
new Date(data.expiresAt)
|
||||
@ -190,13 +239,21 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
||||
/>
|
||||
<AgGridColumn
|
||||
field="createdAt"
|
||||
valueFormatter={({ value }: ValueFormatterParams) => {
|
||||
return getDateTimeFormat().format(new Date(value));
|
||||
valueFormatter={({
|
||||
value,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['createdAt'];
|
||||
}) => {
|
||||
return value ? getDateTimeFormat().format(new Date(value)) : value;
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
field="updatedAt"
|
||||
valueFormatter={({ value }: ValueFormatterParams) => {
|
||||
valueFormatter={({
|
||||
value,
|
||||
}: OrderListTableValueFormatterParams & {
|
||||
value?: Orders_party_ordersConnection_edges_node['updatedAt'];
|
||||
}) => {
|
||||
return value ? getDateTimeFormat().format(new Date(value)) : '-';
|
||||
}}
|
||||
/>
|
||||
|
@ -1,10 +1,11 @@
|
||||
export * from './hooks';
|
||||
export * from './lib/context';
|
||||
export * from './lib/determine-id';
|
||||
export * from './lib/format';
|
||||
export * from './lib/generic-data-provider';
|
||||
export * from './lib/grid';
|
||||
export * from './lib/i18n';
|
||||
export * from './lib/pagination';
|
||||
export * from './lib/remove-0x';
|
||||
export * from './lib/storage';
|
||||
export * from './lib/validate';
|
||||
export * from './lib/determine-id';
|
||||
|
53
libs/react-helpers/src/lib/pagination.ts
Normal file
53
libs/react-helpers/src/lib/pagination.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import type { IGetRowsParams } from 'ag-grid-community';
|
||||
import type { Load } from './generic-data-provider';
|
||||
import type { MutableRefObject } from 'react';
|
||||
|
||||
const getLastRow = (
|
||||
startRow: number,
|
||||
endRow: number,
|
||||
blockLength: number,
|
||||
totalCount?: number
|
||||
) => {
|
||||
let lastRow = -1;
|
||||
if (totalCount !== undefined) {
|
||||
if (!totalCount) {
|
||||
lastRow = 0;
|
||||
} else if (totalCount <= endRow) {
|
||||
lastRow = totalCount;
|
||||
}
|
||||
} else if (blockLength < endRow - startRow) {
|
||||
lastRow = blockLength;
|
||||
}
|
||||
return lastRow;
|
||||
};
|
||||
|
||||
export const makeInfiniteScrollGetRows =
|
||||
<T extends { node: any }>( // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
newRows: MutableRefObject<number>,
|
||||
data: MutableRefObject<(T | null)[] | null>,
|
||||
totalCount: MutableRefObject<number | undefined>,
|
||||
load: Load<(T | null)[]>
|
||||
) =>
|
||||
async ({
|
||||
successCallback,
|
||||
failCallback,
|
||||
startRow,
|
||||
endRow,
|
||||
}: IGetRowsParams) => {
|
||||
startRow += newRows.current;
|
||||
endRow += newRows.current;
|
||||
try {
|
||||
if (data.current && data.current.indexOf(null) < endRow) {
|
||||
await load();
|
||||
}
|
||||
const rowsThisBlock = data.current
|
||||
? data.current.slice(startRow, endRow).map((edge) => edge?.node)
|
||||
: [];
|
||||
successCallback(
|
||||
rowsThisBlock,
|
||||
getLastRow(startRow, endRow, rowsThisBlock.length, totalCount.current)
|
||||
);
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
@ -1,12 +1,11 @@
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useDataProvider,
|
||||
makeInfiniteScrollGetRows,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import type {
|
||||
IGetRowsParams,
|
||||
BodyScrollEvent,
|
||||
BodyScrollEndEvent,
|
||||
} from 'ag-grid-community';
|
||||
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
|
||||
import {
|
||||
MAX_TRADES,
|
||||
tradesDataProvider as dataProvider,
|
||||
@ -100,36 +99,13 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
||||
totalCountRef.current = totalCount;
|
||||
dataRef.current = data;
|
||||
|
||||
const getRows = async ({
|
||||
successCallback,
|
||||
failCallback,
|
||||
startRow,
|
||||
endRow,
|
||||
}: IGetRowsParams) => {
|
||||
startRow += newRows.current;
|
||||
endRow += newRows.current;
|
||||
try {
|
||||
if (dataRef.current && dataRef.current.indexOf(null) < endRow) {
|
||||
await load();
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else if (rowsThisBlock.length < endRow - startRow) {
|
||||
lastRow = rowsThisBlock.length;
|
||||
}
|
||||
successCallback(rowsThisBlock, lastRow);
|
||||
} catch (e) {
|
||||
failCallback();
|
||||
}
|
||||
};
|
||||
const getRows =
|
||||
makeInfiniteScrollGetRows<Trades_market_tradesConnection_edges>(
|
||||
newRows,
|
||||
dataRef,
|
||||
totalCountRef,
|
||||
load
|
||||
);
|
||||
|
||||
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
|
||||
if (event.top === 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user