This repository was archived by the owner on Apr 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e36d758
commit ea58455
Showing
12 changed files
with
295 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/apps/explorer/components/OrdersTableWidget/OrdersTableWithData.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React, { useContext } from 'react' | ||
import { faSpinner } from '@fortawesome/free-solid-svg-icons' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
|
||
import OrdersTable from 'components/orders/OrdersUserDetailsTable' | ||
import { EmptyItemWrapper } from 'components/common/StyledUserDetailsTable' | ||
import { OrdersTableContext, OrdersTableState } from './context/OrdersTableContext' | ||
|
||
export const OrdersTableWithData: React.FC = () => { | ||
const { orders, kind } = useContext(OrdersTableContext) | ||
|
||
return kind === OrdersTableState.Loading ? ( | ||
<EmptyItemWrapper> | ||
<FontAwesomeIcon icon={faSpinner} spin size="3x" /> | ||
</EmptyItemWrapper> | ||
) : ( | ||
<OrdersTable orders={orders} /> | ||
) | ||
} |
17 changes: 17 additions & 0 deletions
17
src/apps/explorer/components/OrdersTableWidget/context/OrdersTableContext.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import React from 'react' | ||
|
||
import { Order } from 'api/operator' | ||
|
||
export enum OrdersTableState { | ||
Loading, | ||
Loaded, | ||
Error, | ||
} | ||
|
||
interface CommonState { | ||
orders: Order[] | ||
error: string | ||
kind: OrdersTableState | ||
} | ||
|
||
export const OrdersTableContext = React.createContext({} as CommonState) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import React, { useMemo } from 'react' | ||
import styled from 'styled-components' | ||
import { useParams } from 'react-router' | ||
import { faSpinner } from '@fortawesome/free-solid-svg-icons' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
|
||
import { media } from 'theme/styles/media' | ||
|
||
import ExplorerTabs from 'apps/explorer/components/common/ExplorerTabs/ExplorerTab' | ||
import { OrdersTableWithData } from './OrdersTableWithData' | ||
import { OrdersTableContext, OrdersTableState } from './context/OrdersTableContext' | ||
import { useGetOrders } from './useGetOrders' | ||
import { TabItemInterface } from 'components/common/Tabs/Tabs' | ||
|
||
const Wrapper = styled.div` | ||
padding: 1.6rem; | ||
margin: 0 auto; | ||
width: 100%; | ||
max-width: 140rem; | ||
${media.mediumDown} { | ||
max-width: 94rem; | ||
} | ||
${media.mobile} { | ||
max-width: 100%; | ||
} | ||
` | ||
|
||
const StyledTabLoader = styled.span` | ||
padding-left: 4px; | ||
` | ||
|
||
const tabItems = (ordersTableState: OrdersTableState): TabItemInterface[] => { | ||
return [ | ||
{ | ||
id: 1, | ||
tab: ( | ||
<> | ||
Orders | ||
<StyledTabLoader> | ||
{ordersTableState === OrdersTableState.Loading && <FontAwesomeIcon icon={faSpinner} spin size="1x" />} | ||
</StyledTabLoader> | ||
</> | ||
), | ||
content: <OrdersTableWithData />, | ||
}, | ||
{ | ||
id: 2, | ||
tab: 'Trades', | ||
content: ( | ||
<> | ||
<h2>Trades Content</h2> | ||
</> | ||
), | ||
}, | ||
] | ||
} | ||
|
||
function useAddressParam(): string { | ||
const { address } = useParams<{ address: string }>() | ||
|
||
return address | ||
} | ||
|
||
const OrdersTableWidget: React.FC = () => { | ||
const ownerAddress = useAddressParam() | ||
const { orders, isLoading, error } = useGetOrders(ownerAddress) | ||
const ordersTableState = useMemo(() => { | ||
if (isLoading && orders.length === 0) return OrdersTableState.Loading | ||
else if (error) return OrdersTableState.Error | ||
|
||
return OrdersTableState.Loaded | ||
}, [isLoading, orders.length, error]) | ||
|
||
return ( | ||
<OrdersTableContext.Provider value={{ orders, kind: ordersTableState, error }}> | ||
<Wrapper> | ||
<ExplorerTabs tabItems={tabItems(isLoading ? OrdersTableState.Loading : OrdersTableState.Loaded)} /> | ||
</Wrapper> | ||
</OrdersTableContext.Provider> | ||
) | ||
} | ||
|
||
export default OrdersTableWidget |
128 changes: 128 additions & 0 deletions
128
src/apps/explorer/components/OrdersTableWidget/useGetOrders.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { useState, useCallback, useEffect } from 'react' | ||
import { subMinutes, getTime } from 'date-fns' | ||
|
||
import { Network } from 'types' | ||
import { useMultipleErc20 } from 'hooks/useErc20' | ||
import { getOrders, Order, RawOrder } from 'api/operator' | ||
import { useNetworkId } from 'state/network' | ||
import { transformOrder } from 'utils' | ||
import { ORDERS_HISTORY_MINUTES_AGO, ORDERS_QUERY_INTERVAL } from 'apps/explorer/const' | ||
|
||
/** | ||
* | ||
* Merge new RawOrders consulted, that may have changed status | ||
* | ||
* @param previousOrders List of orders | ||
* @param newOrdersFetched List of fetched block order that could have changed | ||
*/ | ||
export function mergeNewOrders(previousOrders: Order[], newOrdersFetched: RawOrder[]): Order[] { | ||
if (newOrdersFetched.length === 0) return previousOrders | ||
|
||
// find the order up to which it is to be replaced | ||
const lastOrder = newOrdersFetched[newOrdersFetched.length - 1] | ||
const positionLastOrder = previousOrders.map((o) => o.uid).indexOf(lastOrder.uid) | ||
if (positionLastOrder === -1) { | ||
return newOrdersFetched.map((order) => transformOrder(order)).concat(previousOrders) | ||
} | ||
|
||
const slicedOrders: Order[] = previousOrders.slice(positionLastOrder + 1) | ||
return newOrdersFetched.map((order) => transformOrder(order)).concat(slicedOrders) | ||
} | ||
|
||
function isObjectEmpty(object: Record<string, unknown>): boolean { | ||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars | ||
for (const key in object) { | ||
if (key) return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
type Result = { | ||
orders: Order[] | ||
error: string | ||
isLoading: boolean | ||
} | ||
|
||
export function useGetOrders(ownerAddress: string): Result { | ||
const [isLoading, setIsLoading] = useState(false) | ||
const [error, setError] = useState('') | ||
const [orders, setOrders] = useState<Order[]>([]) | ||
const networkId = useNetworkId() || undefined | ||
const [erc20Addresses, setErc20Addresses] = useState<string[]>([]) | ||
const { value: valueErc20s, isLoading: areErc20Loading } = useMultipleErc20({ networkId, addresses: erc20Addresses }) | ||
const [mountNewOrders, setMountNewOrders] = useState(false) | ||
|
||
const fetchOrders = useCallback( | ||
async (network: Network, owner: string, minTimeHistoryTimeStamp = 0): Promise<void> => { | ||
setIsLoading(true) | ||
setError('') | ||
|
||
try { | ||
const ordersFetched = await getOrders({ networkId: network, owner, minValidTo: minTimeHistoryTimeStamp }) | ||
const newErc20Addresses = ordersFetched.reduce((accumulator: string[], element) => { | ||
const updateAccumulator = (tokenAddress: string): void => { | ||
if (accumulator.indexOf(tokenAddress) === -1) { | ||
accumulator.push(tokenAddress) | ||
} | ||
} | ||
updateAccumulator(element.buyToken) | ||
updateAccumulator(element.sellToken) | ||
|
||
return accumulator | ||
}, []) | ||
|
||
setErc20Addresses(newErc20Addresses) | ||
// For the moment it is neccesary to sort by date | ||
ordersFetched.sort((a, b) => +new Date(b.creationDate) - +new Date(a.creationDate)) | ||
|
||
setOrders((previousOrders) => mergeNewOrders(previousOrders, ordersFetched)) | ||
setMountNewOrders(true) | ||
} catch (e) { | ||
const msg = `Failed to fetch orders` | ||
console.error(msg, e) | ||
setError(msg) | ||
} finally { | ||
setIsLoading(false) | ||
} | ||
}, | ||
[], | ||
) | ||
|
||
useEffect(() => { | ||
if (!networkId) { | ||
return | ||
} | ||
const getOrUpdateOrders = (minHistoryTime?: number): Promise<void> => | ||
fetchOrders(networkId, ownerAddress, minHistoryTime) | ||
|
||
getOrUpdateOrders() | ||
|
||
const intervalId: NodeJS.Timeout = setInterval(() => { | ||
const minutesAgoTimestamp = getTime(subMinutes(new Date(), ORDERS_HISTORY_MINUTES_AGO)) | ||
getOrUpdateOrders(Math.floor(minutesAgoTimestamp / 1000)) | ||
}, ORDERS_QUERY_INTERVAL) | ||
|
||
return (): void => { | ||
clearInterval(intervalId) | ||
} | ||
}, [fetchOrders, networkId, ownerAddress]) | ||
|
||
useEffect(() => { | ||
if (areErc20Loading || isObjectEmpty(valueErc20s) || !mountNewOrders) { | ||
return | ||
} | ||
|
||
const newOrders = orders.map((order) => { | ||
order.buyToken = valueErc20s[order.buyTokenAddress] || order.buyToken | ||
order.sellToken = valueErc20s[order.sellTokenAddress] || order.sellToken | ||
|
||
return order | ||
}) | ||
|
||
setOrders(newOrders) | ||
setMountNewOrders(false) | ||
}, [valueErc20s, networkId, areErc20Loading, mountNewOrders, orders]) | ||
|
||
return { orders, error, isLoading } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
import React from 'react' | ||
|
||
const UserDetails: React.FC = () => <h3>Placeholder Page</h3> | ||
import OrdersTableWidget from '../components/OrdersTableWidget' | ||
|
||
const UserDetails: React.FC = () => <OrdersTableWidget /> | ||
|
||
export default UserDetails |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.