Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to data router, move state to zustand - WIP #484

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license
* SKALE portal
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* @file AppLayout.ts
* @copyright SKALE Labs 2025-Present
*/

import { Box, CssBaseline } from '@mui/material'
import { Outlet } from 'react-router-dom'
import { useMediaQuery } from '@mui/material'
import SkDrawer from './SkDrawer'
import SkBottomNavigation from './SkBottomNavigation'
import Header from './Header'
import { cls, cmn } from '@skalenetwork/metaport'

export default function AppLayout() {
const isXs = useMediaQuery('(max-width: 600px)')

return (
<Box>
<Header />
<Box sx={{ display: 'flex' }} className="AppWrap">
<CssBaseline />
{!isXs && <SkDrawer validatorDelegations={null} />}
<div className={cls(cmn.fullWidth)} id="appContentScroll">
<Outlet />
</div>
</Box>
{isXs && <SkBottomNavigation />}
</Box>
)
}
13 changes: 8 additions & 5 deletions src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Chip from '@mui/material/Chip'
import logo from './assets/skale_lg.svg'

import { constants } from '@/core'
import { cmn, cls, type MetaportCore } from '@skalenetwork/metaport'
import { cmn, cls, useMetaportStore, useWagmiAccount } from '@skalenetwork/metaport'

import HelpZen from './components/HelpZen'
import MoreMenu from './components/MoreMenu'
Expand All @@ -38,7 +38,10 @@ import GetSFuel from './components/GetSFuel'

import { Link } from 'react-router-dom'

export default function Header(props: { address: `0x${string}` | undefined; mpc: MetaportCore }) {
export default function Header() {
const mpc = useMetaportStore((state) => state.mpc)
const { address } = useWagmiAccount()

return (
<AppBar
elevation={0}
Expand All @@ -62,9 +65,9 @@ export default function Header(props: { address: `0x${string}` | undefined; mpc:
/>
) : null}
</div>
<AccountMenu address={props.address} />
<GetSFuel mpc={props.mpc} />
<NetworkSwitch mpc={props.mpc} />
<AccountMenu address={address} />
<GetSFuel mpc={mpc} />
<NetworkSwitch mpc={mpc} />
<HelpZen />
<MoreMenu />
</Toolbar>
Expand Down
6 changes: 2 additions & 4 deletions src/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import './App.scss'

import { BrowserRouter } from 'react-router-dom'
import { inject } from '@vercel/analytics'

inject()

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
<App />
</React.StrictMode>
)
166 changes: 32 additions & 134 deletions src/Portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,148 +21,46 @@
* @copyright SKALE Labs 2023-Present
*/

import { useState, useEffect } from 'react'
import { type types, endpoints } from '@/core'
import {
useMetaportStore,
useWagmiAccount,
Debug,
cls,
cmn,
contracts
} from '@skalenetwork/metaport'

import Box from '@mui/material/Box'
import CssBaseline from '@mui/material/CssBaseline'

import Header from './Header'
import SkDrawer from './SkDrawer'
import Router from './Router'
import SkBottomNavigation from './SkBottomNavigation'
import ProfileModal from './components/profile/ProfileModal'

import { formatSChains } from './core/chain'
import { STATS_API } from './core/constants'
import { getValidatorDelegations } from './core/delegation/staking'
import { getValidator } from './core/delegation'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

import AppLayout from './AppLayout'
import Chains from './pages/Chains'
import { useMetaportStore, useWagmiAccount } from '@skalenetwork/metaport'
import usePortalStore from './PortalStore'
import { useEffect } from 'react'
import Home from './pages/Home'

const router = createBrowserRouter([
{
path: '/',
element: <AppLayout />,
children: [
{
index: true,
element: <Home />
},
{
path: 'chains',
element: <Chains />
}
]
}
])

export default function Portal() {
const mpc = useMetaportStore((state) => state.mpc)

const [schains, setSchains] = useState<types.ISChain[]>([])
const [metrics, setMetrics] = useState<types.IMetrics | null>(null)
const [stats, setStats] = useState<types.IStats | null>(null)
const [validator, setValidator] = useState<types.st.IValidator | null | undefined>(null)
const [validatorDelegations, setValidatorDelegations] = useState<types.st.IDelegation[] | null>(
null
)
const [customAddress, setCustomAddress] = useState<types.AddressType | undefined>(undefined)
const [sc, setSc] = useState<types.st.ISkaleContractsMap | null>(null)
const [loadCalled, setLoadCalled] = useState<boolean>(false)

const endpoint = endpoints.getProxyEndpoint(mpc.config.skaleNetwork)
const statsApi = STATS_API[mpc.config.skaleNetwork]

const { address } = useWagmiAccount()
if (!mpc) return <div></div>

useEffect(() => {
initSkaleContracts()
loadData()
}, [])
const loadData = usePortalStore((state) => state.loadData)

useEffect(() => {
loadValidator()
}, [address, customAddress, sc])

async function initSkaleContracts() {
setLoadCalled(true)
if (loadCalled) return
setSc(await contracts.initContracts(mpc))
}

async function loadChains() {
try {
const response = await fetch(`https://${endpoint}/files/chains.json`)
const chainsJson = await response.json()
setSchains(formatSChains(chainsJson))
} catch (e) {
console.log('Failed to load chains')
console.error(e)
if (mpc && address) {
loadData(mpc, address)
}
}

async function loadMetrics() {
try {
const response = await fetch(`https://${endpoint}/files/metrics.json`)
const metricsJson = await response.json()
setMetrics(metricsJson)
} catch (e) {
console.log('Failed to load metrics')
console.error(e)
}
}

async function loadStats() {
if (statsApi === null) return
try {
const response = await fetch(statsApi)
const statsResp = await response.json()
setStats(statsResp.payload)
} catch (e) {
console.log('Failed to load stats')
console.error(e)
}
}

async function loadValidator() {
const addr = customAddress ?? address
if (!sc || !addr) {
setValidator(null)
setValidatorDelegations(null)
return
}
const validatorData = await getValidator(sc.validatorService, addr)
setValidator(validatorData)
if (validatorData && validatorData.id) {
setValidatorDelegations(await getValidatorDelegations(sc, validatorData.id))
} else {
setValidator(undefined)
setValidatorDelegations(null)
}
}

async function loadData() {
loadChains()
loadMetrics()
loadStats()
loadValidator()
}
}, [mpc, address, loadData])

return (
<Box sx={{ display: 'flex' }} className="AppWrap">
<CssBaseline />
<Header address={address} mpc={mpc} />
<SkDrawer validatorDelegations={validatorDelegations} />
<div className={cls(cmn.fullWidth)} id="appContentScroll">
<Router
loadData={loadData}
schains={schains}
metrics={metrics}
stats={stats}
validator={validator}
validatorDelegations={validatorDelegations}
customAddress={customAddress}
setCustomAddress={setCustomAddress}
sc={sc}
loadValidator={loadValidator}
/>
<ProfileModal />
<div className={cls(cmn.mtop20, cmn.fullWidth)}>
<Debug />
</div>
</div>
<SkBottomNavigation />
</Box>
<div>
<RouterProvider router={router} />
</div>
)
}
54 changes: 54 additions & 0 deletions src/PortalState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @license
* SKALE portal
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* @file PortalState.ts
* @copyright SKALE Labs 2025-Present
*/

import { types } from '@/core'
import { type MetaportCore } from '@skalenetwork/metaport'

export type PortalState = {
schains: types.ISChain[]
metrics: types.IMetrics | null
stats: types.IStats | null
validator: types.st.IValidator | null | undefined
validatorDelegations: types.st.IDelegation[] | null
customAddress: types.AddressType | undefined
sc: types.st.ISkaleContractsMap | null
loadCalled: boolean

chainsMeta: types.ChainsMetadataMap | null
termsAccepted: boolean
stakingTermsAccepted: boolean
validators: types.st.IValidator[]
si: types.st.StakingInfoMap

setCustomAddress: (address: types.AddressType | undefined) => void
setTermsAccepted: (accepted: boolean) => void
setStakingTermsAccepted: (accepted: boolean) => void
initSkaleContracts: (mpc: MetaportCore) => Promise<void>
loadChains: (mpc: MetaportCore) => Promise<void>
loadMetrics: (mpc: MetaportCore) => Promise<void>
loadStats: (mpc: MetaportCore) => Promise<void>
loadValidator: (address: types.AddressType | undefined) => Promise<void>
loadData: (mpc: MetaportCore, address: types.AddressType | undefined) => Promise<void>
loadMetadata: (mpc: MetaportCore) => Promise<void>
loadValidators: () => Promise<void>
loadStakingInfo: (address: types.AddressType | undefined) => Promise<void>
}
Loading
Loading