Files
nym/explorer-nextjs/app/context/main.tsx
T
Fouad e5f41731ae Explorer NextJS Rebuild (#4534)
* bootstrap next app + add overview page

* fix AssetList type

* fix up nav stuff

* Refactor Nav component and add network components pages

* Refactor WorldMap component and update TelegramIcon, GitHubIcon, NymVpnIcon, DiscordIcon, and TwitterIcon components

* add service providers page

* mixnodes page

* delegations page + use material react table for all tables

* nodes map page

* Refactor StyledLink component and remove unnecessary console.log statements

* Refactor ESLint configuration, remove unused dependencies, and update component imports

* update deps

* Refactor imports and update dependencies

* fix dark mode

* build single mixnode page

* build single gateway page

* Refactor handleOnDelegate function to use useCallback in mixnodes page.tsx

* Add defaults for constants

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2024-05-16 16:12:06 +01:00

257 lines
7.1 KiB
TypeScript

'use client'
import * as React from 'react'
import { PaletteMode } from '@mui/material'
import {
ApiState,
BlockResponse,
CountryDataResponse,
GatewayResponse,
MixNodeResponse,
MixnodeStatus,
SummaryOverviewResponse,
ValidatorsResponse,
Environment,
DirectoryServiceProvider,
} from '@/app/typeDefs/explorer-api'
import { EnumFilterKey } from '@/app/typeDefs/filters'
import { Api, getEnvironment } from '@/app/api'
import { toPercentIntegerString } from '@/app/utils'
import { NavOptionType, originalNavOptions } from './nav'
interface StateData {
summaryOverview?: ApiState<SummaryOverviewResponse>
block?: ApiState<BlockResponse>
countryData?: ApiState<CountryDataResponse>
gateways?: ApiState<GatewayResponse>
globalError?: string | undefined
mixnodes?: ApiState<MixNodeResponse>
mode: PaletteMode
validators?: ApiState<ValidatorsResponse>
environment?: Environment
serviceProviders?: ApiState<DirectoryServiceProvider[]>
}
interface StateApi {
fetchMixnodes: (
status?: MixnodeStatus
) => Promise<MixNodeResponse | undefined>
filterMixnodes: (filters: any, status: any) => void
toggleMode: () => void
}
type State = StateData & StateApi
export const MainContext = React.createContext<State>({
mode: 'dark',
toggleMode: () => undefined,
filterMixnodes: () => null,
fetchMixnodes: () => Promise.resolve(undefined),
})
export const useMainContext = (): React.ContextType<typeof MainContext> =>
React.useContext<State>(MainContext)
export const MainContextProvider: FCWithChildren = ({ children }) => {
// network explorer environment
const [environment, setEnvironment] = React.useState<Environment>()
// light/dark mode
const [mode, setMode] = React.useState<PaletteMode>('dark')
// global / banner error messaging
const [globalError] = React.useState<string>()
// various APIs for Overview page
const [summaryOverview, setSummaryOverview] =
React.useState<ApiState<SummaryOverviewResponse>>()
const [mixnodes, setMixnodes] = React.useState<ApiState<MixNodeResponse>>()
const [gateways, setGateways] = React.useState<ApiState<GatewayResponse>>()
const [validators, setValidators] =
React.useState<ApiState<ValidatorsResponse>>()
const [block, setBlock] = React.useState<ApiState<BlockResponse>>()
const [countryData, setCountryData] =
React.useState<ApiState<CountryDataResponse>>()
const [serviceProviders, setServiceProviders] =
React.useState<ApiState<DirectoryServiceProvider[]>>()
const toggleMode = () => setMode((m) => (m !== 'light' ? 'light' : 'dark'))
const fetchOverviewSummary = async () => {
try {
const data = await Api.fetchOverviewSummary()
setSummaryOverview({ data, isLoading: false })
} catch (error) {
setSummaryOverview({
error:
error instanceof Error
? error
: new Error('Overview summary api fail'),
isLoading: false,
})
}
}
const fetchMixnodes = async (status?: MixnodeStatus) => {
let data
setMixnodes((d) => ({ ...d, isLoading: true }))
try {
data = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
setMixnodes({ data, isLoading: false })
} catch (error) {
setMixnodes({
error: error instanceof Error ? error : new Error('Mixnode api fail'),
isLoading: false,
})
}
return data
}
const filterMixnodes = async (
filters: { [key in EnumFilterKey]: number[] },
status?: MixnodeStatus
) => {
setMixnodes((d) => ({ ...d, isLoading: true }))
const mxns = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
const filtered = mxns?.filter(
(m) =>
+m.profit_margin_percent >= filters.profitMargin[0] / 100 &&
+m.profit_margin_percent <= filters.profitMargin[1] / 100 &&
m.stake_saturation >= filters.stakeSaturation[0] &&
m.stake_saturation <= filters.stakeSaturation[1] &&
m.avg_uptime >= filters.routingScore[0] &&
m.avg_uptime <= filters.routingScore[1]
)
setMixnodes({ data: filtered, isLoading: false })
}
const fetchGateways = async () => {
setGateways((d) => ({ ...d, isLoading: true }))
try {
const data = await Api.fetchGateways()
setGateways({ data, isLoading: false })
} catch (error) {
setGateways({
error: error instanceof Error ? error : new Error('Gateways api fail'),
isLoading: false,
})
}
}
const fetchValidators = async () => {
try {
const data = await Api.fetchValidators()
setValidators({ data, isLoading: false })
} catch (error) {
setValidators({
error:
error instanceof Error ? error : new Error('Validators api fail'),
isLoading: false,
})
}
}
const fetchBlock = async () => {
try {
const data = await Api.fetchBlock()
setBlock({ data, isLoading: false })
} catch (error) {
setBlock({
error: error instanceof Error ? error : new Error('Block api fail'),
isLoading: false,
})
}
}
const fetchCountryData = async () => {
setCountryData({ data: undefined, isLoading: true })
try {
const res = await Api.fetchCountryData()
setCountryData({ data: res, isLoading: false })
} catch (error) {
setCountryData({
error:
error instanceof Error ? error : new Error('Country Data api fail'),
isLoading: false,
})
}
}
const fetchServiceProviders = async () => {
setServiceProviders({ data: undefined, isLoading: true })
try {
const res = await Api.fetchServiceProviders()
const resWithRoutingScorePercentage = res.map((item) => ({
...item,
routing_score: item.routing_score
? `${toPercentIntegerString(item.routing_score.toString())}%`
: item.routing_score,
}))
setServiceProviders({
data: resWithRoutingScorePercentage,
isLoading: false,
})
} catch (error) {
setServiceProviders({
error:
error instanceof Error
? error
: new Error('Service provider api fail'),
isLoading: false,
})
}
}
React.useEffect(() => {
if (environment === 'mainnet') {
fetchServiceProviders()
}
}, [environment])
React.useEffect(() => {
setEnvironment(getEnvironment())
Promise.all([
fetchOverviewSummary(),
fetchGateways(),
fetchValidators(),
fetchBlock(),
fetchCountryData(),
])
}, [])
const state = React.useMemo<State>(
() => ({
environment,
block,
countryData,
gateways,
globalError,
mixnodes,
mode,
summaryOverview,
validators,
serviceProviders,
toggleMode,
fetchMixnodes,
filterMixnodes,
}),
[
environment,
block,
countryData,
gateways,
globalError,
mixnodes,
mode,
summaryOverview,
validators,
serviceProviders,
]
)
return <MainContext.Provider value={state}>{children}</MainContext.Provider>
}