Skip to content
This repository has been archived by the owner on Feb 2, 2024. It is now read-only.

Commit

Permalink
236/solvers details (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
matextrem authored Oct 30, 2022
1 parent 60c8fef commit bd80b77
Show file tree
Hide file tree
Showing 12 changed files with 643 additions and 33 deletions.
9 changes: 9 additions & 0 deletions src/apps/explorer/ExplorerApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ const TransactionDetails = React.lazy(
),
)

const SolverDetails = React.lazy(
() =>
import(
/* webpackChunkName: "SolvernDetails_chunk"*/
'./pages/SolverDetails'
),
)

/**
* Update the global state
*/
Expand Down Expand Up @@ -133,6 +141,7 @@ const AppContent = (): JSX.Element => {
<Route path={pathPrefix + '/search/:searchString?'} exact component={SearchNotFound} />
<Route path={pathPrefix + '/appdata'} exact component={AppDataDetails} />
<Route path={pathPrefix + '/solvers'} exact component={Solver} />
<Route path={pathPrefix + '/solvers/:solverAddress'} exact component={SolverDetails} />
<Route component={NotFound} />
</Switch>
</React.Suspense>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useContext, useState, useEffect } from 'react'

import { EmptyItemWrapper } from 'components/common/StyledUserDetailsTable'
import useFirstRender from 'hooks/useFirstRender'
import { useMediaBreakpoint } from 'hooks/useMediaBreakPoint'
import { SettlementsTableContext } from 'apps/explorer/components/SettlementsTableWidget/context/SettlementsTableContext'
import { DEFAULT_TIMEOUT } from 'const'
import SolverDetailsTable from 'components/solver/SolverDetailsTable'
import CowLoading from 'components/common/CowLoading'

export const SolverDetailsTableWithData: React.FC = () => {
const { data: settlements, networkId, tableState } = useContext(SettlementsTableContext)
const isFirstRender = useFirstRender()
const [isFirstLoading, setIsFirstLoading] = useState(true)
const isDesktop = useMediaBreakpoint(['xl', 'lg'])

useEffect(() => {
setIsFirstLoading(true)
}, [networkId])

useEffect(() => {
let timeOutMs = 0
if (!settlements) {
timeOutMs = DEFAULT_TIMEOUT
}

const timeOutId: NodeJS.Timeout = setTimeout(() => {
setIsFirstLoading(false)
}, timeOutMs)

return (): void => {
clearTimeout(timeOutId)
}
}, [settlements, settlements?.length])

return isFirstRender || isFirstLoading ? (
<EmptyItemWrapper>
<CowLoading />
</EmptyItemWrapper>
) : (
<SolverDetailsTable settlements={settlements} tableState={tableState} showBorderTable={isDesktop} />
)
}
78 changes: 78 additions & 0 deletions src/apps/explorer/components/SolverDetailsTableWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react'
import styled from 'styled-components'
import { Network, UiError } from 'types'
import TablePagination from 'apps/explorer/components/common/TablePagination'
import { SolverDetailsTableWithData } from './SolverDetailsTableWithData'
import { SettlementsTableContext } from '../SettlementsTableWidget/context/SettlementsTableContext'
import { useTable } from 'hooks/useTable'
import { Settlement } from 'hooks/useGetSettlements'
import { ConnectionStatus } from 'components/ConnectionStatus'
import { Notification } from 'components/Notification'
import { EmptyItemWrapper } from 'components/common/StyledUserDetailsTable'
import CowLoading from 'components/common/CowLoading'

const Wrapper = styled.div`
display: flex;
flex: 1;
flex-direction: column;
`
const TableHeader = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2rem;
`

type Props = {
settlements: Settlement[]
isLoading: boolean
error: UiError | undefined
networkId: Network | undefined
}

const SolverDetailsTableWidget: React.FC<Props> = ({ settlements, isLoading, error, networkId }) => {
const {
state: tableState,
setPageSize,
handleNextPage,
setPageOffset,
handlePreviousPage,
} = useTable({ initialState: { pageOffset: 0, pageSize: 20 } })

tableState['hasNextPage'] = tableState.pageOffset + tableState.pageSize < settlements.length
tableState['totalResults'] = settlements.length

return (
<SettlementsTableContext.Provider
value={{
data: settlements.slice(tableState.pageOffset, tableState.pageOffset + tableState.pageSize),
error,
isLoading,
networkId,
tableState,
setPageSize,
setPageOffset,
handleNextPage,
handlePreviousPage,
}}
>
<ConnectionStatus />
<Wrapper>
<TableHeader>
<h2>Settlements</h2>
{error && <Notification type={error.type} message={error.message} />}
<TablePagination context={SettlementsTableContext} />
</TableHeader>
{isLoading ? (
<EmptyItemWrapper>
<CowLoading />
</EmptyItemWrapper>
) : (
<SolverDetailsTableWithData />
)}
</Wrapper>
</SettlementsTableContext.Provider>
)
}

export default SolverDetailsTableWidget
11 changes: 11 additions & 0 deletions src/apps/explorer/pages/Solver/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ const Solver: React.FC = () => {
</WrapperExtraComponents>
)

useEffect(() => {
if (query.length) {
setPageOffset(0)
}
}, [query, setPageOffset])

useEffect(() => {
setQuery('')
setPageOffset(0)
}, [networkId, setPageOffset, setQuery, tabViewSelected])

const onChangeTab = useCallback((tabId: number) => {
const newTabViewName = TabView[tabId]
if (!newTabViewName) return
Expand Down
159 changes: 159 additions & 0 deletions src/apps/explorer/pages/SolverDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React from 'react'
import styled from 'styled-components'
import { useParams } from 'react-router'
import { Helmet } from 'react-helmet'
import { isAddress } from 'web3-utils'
import { abbreviateString } from 'utils'
import { useGetSettlements } from 'hooks/useGetSettlements'
import { useSolversInfo } from 'hooks/useSolversInfo'
import { useMediaBreakpoint } from 'hooks/useMediaBreakPoint'
import { media } from 'theme/styles/media'

import RedirectToSearch from 'components/RedirectToSearch'
import { Card, CardContent } from 'components/common/Card'
import { CardRow } from 'components/common/CardRow'
import { DateDisplay } from 'components/common/DateDisplay'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import Identicon from 'components/common/Identicon'
import { HelpTooltip } from 'components/Tooltip'
import { Wrapper, FlexContainerVar, TitleAddress } from 'apps/explorer/pages/styled'
import SolverDetailsTableWidget from 'apps/explorer/components/SolverDetailsTableWidget'
import { numberFormatter } from 'apps/explorer/components/SummaryCardsWidget/utils'
import { APP_TITLE } from 'apps/explorer/const'
import { useNetworkId } from 'state/network'

const DESKTOP_TEXT_SIZE = 1.8 // rem
const MOBILE_TEXT_SIZE = 1.65 // rem

const HeaderContainer = styled.div`
display: flex;
align-items: center;
span:first-child {
margin: 0 0 0 0.5rem;
}
`

const StyledCard = styled(Card)`
${media.mediumDown} {
.card-container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 60px;
}
}
@media (max-width: 600px) {
.card-container {
height: auto;
}
}
`

const AddressContainer = styled(TitleAddress)`
margin: 0 0 0 1rem;
`
const SolverInfoContainer = styled.div`
display: flex;
align-items: baseline;
`

const TableContainer = styled.div`
display: flex;
align-items: center;
margin-top: 6rem;
`
const SolverName = styled.p`
font-size: 1.6rem;
margin: 0 0 0 1rem;
`

const SolverDetails: React.FC = () => {
const { solverAddress } = useParams<{ solverAddress: string }>()
const networkId = useNetworkId() || undefined
const solversInfo = useSolversInfo(networkId)
const { settlements, isLoading, error } = useGetSettlements(networkId, [], solverAddress)
const solverName = solversInfo?.find((solver) => solver.address.toLowerCase() === solverAddress.toLowerCase())?.name
const totalVolumeUSD = settlements.reduce((acc, settlement) => acc + settlement.totalVolumeUsd, 0)
const isDesktop = useMediaBreakpoint(['xl', 'lg'])
const valueTextSize = isDesktop ? DESKTOP_TEXT_SIZE : MOBILE_TEXT_SIZE

if (!isAddress(solverAddress.toLowerCase())) {
return <RedirectToSearch from="solvers" />
}

return (
<Wrapper>
<Helmet>
<title>Solver Details - {APP_TITLE}</title>
</Helmet>
<h1>Solver Details</h1>
<FlexContainerVar>
<HeaderContainer>
<Identicon address={solverAddress} size="md" />
<SolverInfoContainer>
{solverName && <SolverName>{solverName}</SolverName>}
<AddressContainer
textToCopy={solverAddress}
contentsToDisplay={
<BlockExplorerLink
showLogo
type="address"
networkId={networkId}
identifier={solverAddress}
label={abbreviateString(solverAddress, 6, 4)}
/>
}
/>
</SolverInfoContainer>
</HeaderContainer>
</FlexContainerVar>
<CardRow>
<StyledCard xs={6} sm={3} md={3} lg={3}>
<CardContent
variant="double"
label1="Total trades"
icon1={<HelpTooltip tooltip="Total trades settled by this solver" />}
value1={numberFormatter(settlements[0]?.solver.numberOfTrades || 0)}
loading={isLoading}
valueSize={valueTextSize}
/>
</StyledCard>
<StyledCard xs={6} sm={3} md={3} lg={3}>
<CardContent
variant="double"
icon1={<HelpTooltip tooltip="Total volume settled by this solver" />}
label1="Total volume"
value1={`$${Number(totalVolumeUSD) ? numberFormatter(totalVolumeUSD) : 0}`}
loading={isLoading}
valueSize={valueTextSize}
/>
</StyledCard>
<StyledCard xs={6} sm={3} md={3} lg={3}>
<CardContent
variant="double"
label1="Total settlements"
icon1={<HelpTooltip tooltip="Total settlements by this solver" />}
value1={numberFormatter(settlements.length)}
loading={isLoading}
valueSize={valueTextSize}
/>
</StyledCard>
<StyledCard xs={6} sm={3} md={3} lg={3}>
<CardContent
variant="double"
label1="Active since"
icon1={<HelpTooltip tooltip="When the solver became active as was able to submit solutions" />}
value1={<DateDisplay date={new Date()} showIcon={true} />}
loading={isLoading}
valueSize={valueTextSize}
/>
</StyledCard>
</CardRow>
<TableContainer>
<SolverDetailsTableWidget settlements={settlements} isLoading={isLoading} error={error} networkId={networkId} />
</TableContainer>
</Wrapper>
)
}

export default SolverDetails
8 changes: 4 additions & 4 deletions src/components/common/Card/CardContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ export const CardContent: React.FC<CardContentProps> = ({
captionColor={captionColor}
hintColor={hintColor}
>
<div>
<div className="card-container">
<p>
{icon1 && <React.Fragment>{icon1} &nbsp;</React.Fragment>}
{label1}
<span style={{ textAlign: 'center' }}>{label1}</span>
</p>
<div>
<div style={{ textAlign: 'center' }}>
{loading ? <ShimmerBar /> : <h3>{value1}</h3>}
{!loading && caption1 && (
<span>
Expand All @@ -136,7 +136,7 @@ export const CardContent: React.FC<CardContentProps> = ({
</div>
</div>
{!loading && label2 && (
<div>
<div style={{ textAlign: 'center' }}>
<p>
{label2}
{icon2 && <React.Fragment>{icon2} &nbsp;</React.Fragment>}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/DateDisplay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function DateDisplay({ date, showIcon, tooltipPlacement = 'top' }: DateDi
// pp: Long localized time: 12:00:00 AM
// For reference: https://date-fns.org/v2.17.0/docs/format
const fullLocaleBased = format(date, 'P pp zzzz')
const previewDate = format(date, 'd MMMM yyyy - h:mm a')
const previewDate = format(date, 'd MMMM yyyy - HH:mm')
return (
<span className="wrap-datedisplay">
<Tooltip {...tooltipProps}>
Expand Down
5 changes: 4 additions & 1 deletion src/components/solver/ActiveSolverTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import StyledUserDetailsTable, {
import { media } from 'theme/styles/media'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import { RowWithCopyButton } from 'components/common/RowWithCopyButton'
import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork'
import Identicon from 'components/common/Identicon'
import { numberFormatter } from 'apps/explorer/components/SummaryCardsWidget/utils'
import { TextWithTooltip } from 'apps/explorer/components/common/TextWithTooltip'
Expand Down Expand Up @@ -193,7 +194,9 @@ const RowSolver: React.FC<RowProps> = ({ solver }) => {
<HeaderValue>
<IdenticonWrapper>
<Identicon address={address} size="md" />
<BlockExplorerLink type="address" networkId={network} identifier={address} label={name} />
<LinkWithPrefixNetwork to={`/solvers/${address}`} rel="noopener noreferrer" target="_self">
{name || abbreviateString(address, 4, 6)}
</LinkWithPrefixNetwork>
</IdenticonWrapper>
</HeaderValue>
</td>
Expand Down
5 changes: 3 additions & 2 deletions src/components/solver/SettlementTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import StyledUserDetailsTable, {
} from '../../common/StyledUserDetailsTable'

import { media } from 'theme/styles/media'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork'
import { DateDisplay } from 'components/common/DateDisplay'
import { RowWithCopyButton } from 'components/common/RowWithCopyButton'
Expand Down Expand Up @@ -205,7 +204,9 @@ const RowSettlement: React.FC<RowProps> = ({ settlement }) => {
<HeaderValue>
<IdenticonWrapper>
<Identicon address={address} size="md" />
<BlockExplorerLink type="address" networkId={network} identifier={address} label={name} />
<LinkWithPrefixNetwork to={`/solvers/${address}`} rel="noopener noreferrer" target="_self">
{name || abbreviateString(address, 4, 6)}
</LinkWithPrefixNetwork>
</IdenticonWrapper>
</HeaderValue>
</td>
Expand Down
Loading

0 comments on commit bd80b77

Please sign in to comment.