Task/Strict mode enabled for trading app (#150)

* enable strict mode and fix resulting type errors

* fix print affected command

* remove assign-deep and use lodash/merge, fix some type errors after enabling strict mode
This commit is contained in:
Matthew Russell 2022-03-28 12:34:45 -07:00 committed by GitHub
parent 859dc60133
commit d4652b3dd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 108 additions and 78 deletions

View File

@ -3,8 +3,8 @@ import { ReactNode } from 'react';
interface AsyncRendererProps<T> {
loading: boolean;
error: Error | undefined;
data: T;
error: Error | undefined | null;
data: T | undefined;
children: (data: T) => ReactNode;
}
@ -19,7 +19,7 @@ export function AsyncRenderer<T = any>({
return <Splash>Something went wrong: {error.message}</Splash>;
}
if (loading) {
if (loading || !data) {
return <Splash>Loading...</Splash>;
}

View File

@ -1,12 +1,16 @@
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
import { DealTicket } from '@vegaprotocol/deal-ticket';
import { OrderStatus } from '@vegaprotocol/graphql';
import { Market_market, OrderStatus } from '@vegaprotocol/graphql';
import { useOrderSubmit } from '../../hooks/use-order-submit';
import { useEffect, useState } from 'react';
import { VegaTxStatus } from '../../hooks/use-vega-transaction';
import { OrderDialog } from './order-dialog';
export const DealTicketContainer = ({ market }) => {
interface DealTicketContainerProps {
market: Market_market;
}
export const DealTicketContainer = ({ market }: DealTicketContainerProps) => {
const [orderDialogOpen, setOrderDialogOpen] = useState(false);
const { submit, transaction, finalizedOrder, reset } = useOrderSubmit(market);

View File

@ -66,10 +66,10 @@ export const OrderDialog = ({
icon={<Icon name="tick" size={20} />}
>
<p>Status: {finalizedOrder.status}</p>
<p>Market: {finalizedOrder.market.name}</p>
{finalizedOrder.market && <p>Market: {finalizedOrder.market.name}</p>}
<p>Type: {finalizedOrder.type}</p>
<p>Amount: {finalizedOrder.size}</p>
{finalizedOrder.type === 'Limit' && (
{finalizedOrder.type === 'Limit' && finalizedOrder.market && (
<p>
Price:{' '}
{formatNumber(

View File

@ -17,11 +17,7 @@ export const PageQueryContainer = <TData, TVariables = OperationVariables>({
const { data, loading, error } = useQuery<TData, TVariables>(query, options);
return (
<AsyncRenderer<TData>
loading={loading || Boolean(!data)}
error={error}
data={data}
>
<AsyncRenderer<TData> loading={loading} error={error} data={data}>
{(data) => children(data)}
</AsyncRenderer>
);

View File

@ -33,7 +33,9 @@ export const Web3Content = ({ children, setDialogOpen }: Web3ContentProps) => {
const { isActive, error, connector, chainId } = useWeb3React();
useEffect(() => {
connector?.connectEagerly();
if (typeof connector?.connectEagerly === 'function') {
connector.connectEagerly();
}
}, [connector]);
if (error) {

View File

@ -54,7 +54,7 @@ export const useOrderSubmit = (market: UseOrderSubmitMarket) => {
variables: { partyId: keypair?.pub || '' },
skip: !id,
onSubscriptionData: ({ subscriptionData }) => {
if (!subscriptionData.data.busEvents.length) {
if (!subscriptionData.data?.busEvents?.length) {
return;
}
@ -100,9 +100,9 @@ export const useOrderSubmit = (market: UseOrderSubmitMarket) => {
orderSubmission: {
marketId: market.id,
price:
order.type === OrderType.Market
? undefined
: removeDecimal(order.price, market.decimalPlaces),
order.type === OrderType.Limit && order.price
? removeDecimal(order.price, market.decimalPlaces)
: undefined,
size: order.size,
type: order.type,
side: order.side,

View File

@ -103,11 +103,13 @@ export const useOrders = (): UseOrders => {
variables: { partyId: keypair.pub },
});
if (!res.data.party?.orders.length) return;
if (!res.data.party?.orders?.length) return;
mergeOrders(res.data.party.orders);
} catch (err) {
setError(err);
setError(
err instanceof Error ? err : new Error('Something went wrong')
);
} finally {
setLoading(false);
}
@ -126,6 +128,9 @@ export const useOrders = (): UseOrders => {
variables: { partyId: keypair.pub },
})
.subscribe(({ data }) => {
if (!data?.orders) {
return;
}
mergeOrders(data.orders);
});

View File

@ -60,7 +60,7 @@ export const useVegaTransaction = () => {
} else if ('errors' in res) {
handleError(res);
return null;
} else if (res.tx && res.txHash) {
} else if (res.tx?.signature?.value && res.txHash) {
setTransaction({
status: VegaTxStatus.Pending,
hash: res.txHash,

View File

@ -8,9 +8,15 @@ export function useEagerConnect() {
useEffect(() => {
const cfg = LocalStorage.getItem(WALLET_CONFIG);
const cfgObj = JSON.parse(cfg);
let cfgObj: { connector: 'rest'; token: string } | null;
// No stored config, user has never connected or manually cleared storage
try {
cfgObj = cfg ? JSON.parse(cfg) : null;
} catch {
cfgObj = null;
}
// No stored config, or config was malformed
if (!cfgObj || !cfgObj.connector) {
return;
}

View File

@ -1,5 +1,6 @@
import {
ApolloClient,
ApolloLink,
split,
from,
HttpLink,
@ -60,7 +61,7 @@ export function createClient(base?: string) {
url: urlWS.href,
})
)
: null;
: new ApolloLink((operation, forward) => forward(operation));
const splitLink = process.browser
? split(

View File

@ -46,21 +46,29 @@ const MARKET_QUERY = gql`
`;
const MarketPage = () => {
const {
query: { marketId },
} = useRouter();
const { query } = useRouter();
const { w } = useWindowSize();
// Default to first marketId query item if found
const marketId = Array.isArray(query.marketId)
? query.marketId[0]
: query.marketId;
if (!marketId) {
return (
<Splash>
<p>Not found</p>
</Splash>
);
}
return (
<PageQueryContainer<Market, MarketVariables>
query={MARKET_QUERY}
options={{
// Not sure exactly why marketId is string | string[] but just the first item in the array if
// it is one
variables: {
marketId: Array.isArray(marketId) ? marketId[0] : marketId,
marketId,
},
skip: !marketId,
fetchPolicy: 'network-only',
}}
>

View File

@ -4,21 +4,24 @@ import { useRouter } from 'next/router';
import {
Children,
isValidElement,
ReactElement,
ReactNode,
useEffect,
useState,
} from 'react';
interface GridTabsProps {
children: ReactNode;
children: ReactElement<GridTabProps>[];
group: string;
}
export const GridTabs = ({ children, group }: GridTabsProps) => {
const { query, asPath, replace } = useRouter();
const [activeTab, setActiveTab] = useState<string>(() => {
if (query[group]) {
return query[group];
const tab = query[group];
if (typeof tab === 'string') {
return tab;
}
// Default to first tab

View File

@ -1,6 +1,6 @@
import { useState, useEffect, useRef } from 'react';
import { produce } from 'immer';
import assign from 'assign-deep';
import merge from 'lodash/merge';
import { useApolloClient } from '@apollo/client';
import { useRouter } from 'next/router';
import { AsyncRenderer } from '../../components/async-renderer';
@ -16,11 +16,11 @@ import type { AgGridReact } from 'ag-grid-react';
const Markets = () => {
const { pathname, push } = useRouter();
const [markets, setMarkets] = useState<Markets_markets[]>(undefined);
const [markets, setMarkets] = useState<Markets_markets[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error>(undefined);
const [error, setError] = useState<Error>();
const client = useApolloClient();
const gridRef = useRef<AgGridReact>();
const gridRef = useRef<AgGridReact | null>(null);
const initialized = useRef<boolean>(false);
useEffect(() => {
@ -38,7 +38,8 @@ const Markets = () => {
const add: Markets_markets[] = [];
// split into updates and adds
if (!gridRef.current) return;
if (!gridRef.current || !delta) return;
const rowNode = gridRef.current.api.getRowNode(
getRowNodeId(delta.market)
);
@ -46,7 +47,7 @@ const Markets = () => {
if (rowNode) {
const updatedData = produce(
rowNode.data.data,
(draft: Markets_markets_data) => assign(draft, delta)
(draft: Markets_markets_data) => merge(draft, delta)
);
if (updatedData !== rowNode.data.data) {
update.push({ ...rowNode.data, data: delta });

View File

@ -1,7 +1,7 @@
import { Market_market } from '@vegaprotocol/graphql';
import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer';
import { useState, ReactNode } from 'react';
import { useState, ReactNode, ComponentType } from 'react';
import { GridTab, GridTabs } from './grid-tabs';
import { DealTicketContainer } from '../../components/deal-ticket-container';
import { OrderListContainer } from '../..//components/order-list-container';
@ -33,18 +33,28 @@ const Trades = () => (
</Splash>
);
type TradingView = keyof typeof TradingViews;
// enum TradingView {
// Chart = 'Chart',
// Ticket = 'Ticket',
// Orderbook = 'Orderbook',
// Orders = 'Orders',
// Positions = 'Positions',
// Collateral = 'Collateral',
// Trades = 'Trades',
// }
const TradingViews = {
chart: Chart,
ticket: DealTicketContainer,
orderbook: Orderbook,
orders: OrderListContainer,
positions: Positions,
collateral: Collateral,
trades: Trades,
Chart: Chart,
Ticket: DealTicketContainer,
Orderbook: Orderbook,
Orders: OrderListContainer,
Positions: Positions,
Collateral: Collateral,
Trades: Trades,
};
type TradingView = keyof typeof TradingViews;
interface TradeGridProps {
market: Market_market;
}
@ -62,31 +72,31 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
<h1>Market: {market.name}</h1>
</header>
<TradeGridChild className="col-start-1 col-end-2">
<TradingViews.chart />
<TradingViews.Chart />
</TradeGridChild>
<TradeGridChild className="row-start-1 row-end-3">
<TradingViews.ticket market={market} />
<TradingViews.Ticket market={market} />
</TradeGridChild>
<TradeGridChild className="row-start-1 row-end-3">
<GridTabs group="trade">
<GridTab name="trades">
<TradingViews.trades />
<TradingViews.Trades />
</GridTab>
<GridTab name="orderbook">
<TradingViews.orderbook />
<TradingViews.Orderbook />
</GridTab>
</GridTabs>
</TradeGridChild>
<TradeGridChild className="col-span-3">
<GridTabs group="portfolio">
<GridTab name="orders">
<TradingViews.orders />
<TradingViews.Orders />
</GridTab>
<GridTab name="positions">
<TradingViews.positions />
<TradingViews.Positions />
</GridTab>
<GridTab name="collateral">
<TradingViews.collateral />
<TradingViews.Collateral />
</GridTab>
</GridTabs>
</TradeGridChild>
@ -119,7 +129,7 @@ interface TradePanelsProps {
}
export const TradePanels = ({ market }: TradePanelsProps) => {
const [view, setView] = useState<TradingView>('chart');
const [view, setView] = useState<TradingView>('Chart');
const renderView = () => {
const Component = TradingViews[view];
@ -144,7 +154,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
</AutoSizer>
</div>
<div className="flex flex-nowrap gap-4 overflow-x-auto my-4 max-w-full">
{Object.keys(TradingViews).map((key: TradingView) => {
{Object.keys(TradingViews).map((key) => {
const isActive = view === key;
const className = classNames('py-4', 'px-12', 'capitalize', {
'text-black dark:text-vega-yellow': isActive,
@ -154,7 +164,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
});
return (
<button
onClick={() => setView(key)}
onClick={() => setView(key as TradingView)}
className={className}
key={key}
>

View File

@ -13,14 +13,14 @@ const Deposit = () => {
};
const Info = () => {
const { isActive, chainId, accounts } = useWeb3React();
const { isActive, chainId, account } = useWeb3React();
if (!isActive) {
return <div>Not active</div>;
}
return (
<div>
<p>{chainId}</p>
<p>{accounts[0]}</p>
<p>{account ? account : 'No account'}</p>
</div>
);
};

View File

@ -6,7 +6,7 @@
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["node", "jest"],
"strict": false,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"resolveJsonModule": true,

View File

@ -1,6 +1,6 @@
const currentProject = process.env.NX_PROJECT_NAME;
const execSync = require('child_process').execSync;
const getAffected = `nx print-affected`;
const getAffected = `npx nx print-affected`;
const output = execSync(getAffected).toString();
//get the list of changed projects from the output
const changedProjects = JSON.parse(output).projects; // array of affected projects

View File

@ -5,10 +5,9 @@ import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { Markets_markets } from '@vegaprotocol/graphql';
import { AgGridColumn } from 'ag-grid-react';
import type { AgGridReact } from 'ag-grid-react';
import { useState } from 'react';
interface MarketListTableProps {
markets: Markets_markets[];
markets: Markets_markets[] | null;
onRowClicked: (marketId: string) => void;
}

View File

@ -31,7 +31,6 @@
"ag-grid-community": "^27.0.1",
"ag-grid-react": "^27.0.1",
"apollo": "^2.33.9",
"assign-deep": "^1.0.1",
"autoprefixer": "^10.4.2",
"bignumber.js": "^9.0.2",
"classnames": "^2.3.1",
@ -41,8 +40,8 @@
"ethers": "^5.6.0",
"graphql": "^15.7.2",
"graphql-ws": "^5.6.3",
"lodash": "^4.17.21",
"immer": "^9.0.12",
"lodash": "^4.17.21",
"next": "12.0.7",
"nx": "^13.8.3",
"postcss": "^8.4.6",
@ -89,6 +88,7 @@
"@types/prismjs": "^1.26.0",
"@types/react": "17.0.30",
"@types/react-dom": "17.0.9",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"babel-jest": "27.2.3",

View File

@ -5394,6 +5394,13 @@
dependencies:
"@types/react" "*"
"@types/react-virtualized-auto-sizer@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz#b3187dae1dfc4c15880c9cfc5b45f2719ea6ebd4"
integrity sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==
dependencies:
"@types/react" "*"
"@types/react@*":
version "17.0.39"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
@ -7026,23 +7033,11 @@ assertion-error@^1.1.0:
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
assign-deep@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/assign-deep/-/assign-deep-1.0.1.tgz#b6d21d74e2f28bf6592e4c0c541bed6ab59c5f27"
integrity sha512-CSXAX79mibneEYfqLT5FEmkqR5WXF+xDRjgQQuVf6wSCXCYU8/vHttPidNar7wJ5BFmKAo8Wei0rCtzb+M/yeA==
dependencies:
assign-symbols "^2.0.2"
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
assign-symbols@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-2.0.2.tgz#0fb9191dd9d617042746ecfc354f3a3d768a0c98"
integrity sha512-9sBQUQZMKFKcO/C3Bo6Rx4CQany0R0UeVcefNGRRdW2vbmaMOhV1sbmlXcQLcD56juLXbSGTBm0GGuvmrAF8pA==
ast-types-flow@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"