From 6f6940fd3ca3b7131ff140dcf3353ad9bb26263a Mon Sep 17 00:00:00 2001 From: Dmytro Date: Mon, 17 Mar 2025 19:17:02 +0000 Subject: [PATCH] Change router to data router, move state to zustand --- src/AppLayout.tsx | 47 ++++++ src/Header.tsx | 13 +- src/Main.tsx | 6 +- src/Portal.tsx | 166 ++++--------------- src/PortalState.ts | 54 +++++++ src/PortalStore.ts | 162 +++++++++++++++++++ src/Router.tsx | 372 ------------------------------------------- src/pages/Chains.tsx | 69 ++++---- src/pages/Home.tsx | 42 +++-- src/useApps.tsx | 7 +- 10 files changed, 366 insertions(+), 572 deletions(-) create mode 100644 src/AppLayout.tsx create mode 100644 src/PortalState.ts create mode 100644 src/PortalStore.ts delete mode 100644 src/Router.tsx diff --git a/src/AppLayout.tsx b/src/AppLayout.tsx new file mode 100644 index 00000000..43295849 --- /dev/null +++ b/src/AppLayout.tsx @@ -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 . + */ +/** + * @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 ( + +
+ + + {!isXs && } +
+ +
+
+ {isXs && } + + ) +} diff --git a/src/Header.tsx b/src/Header.tsx index 47743b48..ed9efe06 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -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' @@ -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 ( ) : null} - - - + + + diff --git a/src/Main.tsx b/src/Main.tsx index 7b8ba914..03546982 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -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( - - - + ) diff --git a/src/Portal.tsx b/src/Portal.tsx index 12d957ca..46c5d342 100644 --- a/src/Portal.tsx +++ b/src/Portal.tsx @@ -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: , + children: [ + { + index: true, + element: + }, + { + path: 'chains', + element: + } + ] + } +]) export default function Portal() { const mpc = useMetaportStore((state) => state.mpc) - - const [schains, setSchains] = useState([]) - const [metrics, setMetrics] = useState(null) - const [stats, setStats] = useState(null) - const [validator, setValidator] = useState(null) - const [validatorDelegations, setValidatorDelegations] = useState( - null - ) - const [customAddress, setCustomAddress] = useState(undefined) - const [sc, setSc] = useState(null) - const [loadCalled, setLoadCalled] = useState(false) - - const endpoint = endpoints.getProxyEndpoint(mpc.config.skaleNetwork) - const statsApi = STATS_API[mpc.config.skaleNetwork] - const { address } = useWagmiAccount() - if (!mpc) return
- - 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 ( - - -
- -
- - -
- -
-
- - +
+ +
) } diff --git a/src/PortalState.ts b/src/PortalState.ts new file mode 100644 index 00000000..5ae62a5d --- /dev/null +++ b/src/PortalState.ts @@ -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 . + */ +/** + * @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 + loadChains: (mpc: MetaportCore) => Promise + loadMetrics: (mpc: MetaportCore) => Promise + loadStats: (mpc: MetaportCore) => Promise + loadValidator: (address: types.AddressType | undefined) => Promise + loadData: (mpc: MetaportCore, address: types.AddressType | undefined) => Promise + loadMetadata: (mpc: MetaportCore) => Promise + loadValidators: () => Promise + loadStakingInfo: (address: types.AddressType | undefined) => Promise +} diff --git a/src/PortalStore.ts b/src/PortalStore.ts new file mode 100644 index 00000000..20930773 --- /dev/null +++ b/src/PortalStore.ts @@ -0,0 +1,162 @@ +/** + * @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 . + */ +/** + * @file PortalStore.ts + * @copyright SKALE Labs 2025-Present + */ + +import { create } from 'zustand' +import { endpoints, metadata } from '@/core' +import { contracts, type MetaportCore } from '@skalenetwork/metaport' + +import { formatSChains } from './core/chain' +import { getValidatorDelegations } from './core/delegation/staking' +import { getValidators, getValidator } from './core/delegation/validators' +import { getStakingInfoMap } from './core/delegation/staking' +import { STATS_API } from './core/constants' + +import { PortalState } from './PortalState' + +const usePortalStore = create()((set, get) => ({ + schains: [], + metrics: null, + stats: null, + validator: null, + validatorDelegations: null, + customAddress: undefined, + sc: null, + loadCalled: false, + + chainsMeta: null, + termsAccepted: false, + stakingTermsAccepted: false, + validators: [], + si: { 0: null, 1: null, 2: null }, + + setCustomAddress: (address) => set({ customAddress: address }), + setTermsAccepted: (accepted) => set({ termsAccepted: accepted }), + setStakingTermsAccepted: (accepted) => set({ stakingTermsAccepted: accepted }), + + initSkaleContracts: async (mpc: MetaportCore) => { + if (get().loadCalled) return + set({ loadCalled: true }) + try { + const sc = await contracts.initContracts(mpc) + set({ sc }) + } catch (e) { + console.error('Failed to initialize SKALE contracts', e) + } + }, + + loadChains: async (mpc: MetaportCore) => { + try { + const endpoint = endpoints.getProxyEndpoint(mpc.config.skaleNetwork) + const response = await fetch(`https://${endpoint}/files/chains.json`) + const chainsJson = await response.json() + set({ schains: formatSChains(chainsJson) }) + } catch (e) { + console.error('Failed to load chains', e) + } + }, + + loadMetrics: async (mpc: MetaportCore) => { + try { + const endpoint = endpoints.getProxyEndpoint(mpc.config.skaleNetwork) + const response = await fetch(`https://${endpoint}/files/metrics.json`) + const metricsJson = await response.json() + set({ metrics: metricsJson }) + } catch (e) { + console.error('Failed to load metrics', e) + } + }, + + loadStats: async (mpc: MetaportCore) => { + const statsApi = STATS_API[mpc.config.skaleNetwork] + if (!statsApi) return + try { + const response = await fetch(statsApi) + const statsResp = await response.json() + set({ stats: statsResp.payload }) + } catch (e) { + console.error('Failed to load stats', e) + } + }, + + loadValidator: async (address) => { + const sc = get().sc + if (!sc || !address) { + set({ validator: null, validatorDelegations: null }) + return + } + try { + const validatorData = await getValidator(sc.validatorService, address) + set({ validator: validatorData }) + if (validatorData && validatorData.id) { + const delegations = await getValidatorDelegations(sc, validatorData.id) + set({ validatorDelegations: delegations }) + } else { + set({ validator: undefined, validatorDelegations: null }) + } + } catch (e) { + console.error('Failed to load validator', e) + } + }, + + loadData: async (mpc, address) => { + await Promise.all([ + get().loadChains(mpc), + get().loadMetrics(mpc), + get().loadStats(mpc), + get().loadValidator(address), + get().loadMetadata(mpc) + ]) + }, + + loadMetadata: async (mpc: MetaportCore) => { + try { + const chainsMeta = await metadata.loadMeta(mpc.config.skaleNetwork) + set({ chainsMeta: chainsMeta }) + } catch (e) { + console.error('Failed to load metadata', e) + } + }, + + loadValidators: async () => { + const sc = get().sc + if (!sc) return + try { + const validatorsData = await getValidators(sc.validatorService) + set({ validators: validatorsData }) + } catch (e) { + console.error('Failed to load validators', e) + } + }, + + loadStakingInfo: async (address) => { + const sc = get().sc + if (!sc) return + try { + const si = await getStakingInfoMap(sc, address) + set({ si }) + } catch (e) { + console.error('Failed to load staking info', e) + } + } +})) + +export default usePortalStore diff --git a/src/Router.tsx b/src/Router.tsx deleted file mode 100644 index cf6b2c0c..00000000 --- a/src/Router.tsx +++ /dev/null @@ -1,372 +0,0 @@ -/** - * @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 . - */ -/** - * @file Router.tsx - * @copyright SKALE Labs 2024-Present - */ - -import './App.scss' - -import { useState, useEffect } from 'react' - -import { Helmet } from 'react-helmet' -import { useLocation, Routes, Route, Navigate, useSearchParams } from 'react-router-dom' -import { TransitionGroup, CSSTransition } from 'react-transition-group' - -import { useTheme } from '@mui/material/styles' -import useMediaQuery from '@mui/material/useMediaQuery' -import { CircularProgress } from '@mui/material' - -import { - useMetaportStore, - type MetaportState, - useWagmiAccount, - useWagmiWalletClient, - useWagmiSwitchNetwork, - walletClientToSigner, - enforceNetwork, - cls, - cmn -} from '@skalenetwork/metaport' - -import { type types, metadata, constants } from '@/core' - -import Bridge from './pages/Bridge' -import Faq from './pages/Faq' -import Terms from './pages/Terms' -import Chains from './pages/Chains' -import Chain from './pages/Chain' -import Stats from './pages/Stats' -import Ecosystem from './pages/Ecosystem' -import App from './pages/App' -import History from './pages/History' -import Portfolio from './pages/Portfolio' -import Admin from './pages/Admin' -import Start from './pages/Home' -import Staking from './pages/Staking' -import StakeValidator from './pages/StakeValidator' -import StakeAmount from './pages/StakeAmount' -import Validators from './pages/Validators' -import Validator from './pages/Validator' -import Onramp from './pages/Onramp' -import TermsModal from './components/TermsModal' -import Changelog from './pages/Changelog' - -import MetricsWarning from './components/MetricsWarning' -import ScrollToTop from './components/ScrollToTop' - -import { getHistoryFromStorage, setHistoryToStorage } from './core/transferHistory' -import { BRIDGE_PAGES, STAKING_PAGES } from './core/constants' -import { getValidators } from './core/delegation/validators' -import { getStakingInfoMap } from './core/delegation/staking' - -export default function Router(props: { - loadData: () => Promise - customAddress?: types.AddressType - setCustomAddress: (address: types.AddressType) => void - schains: types.ISChain[] - stats: types.IStats | null - metrics: types.IMetrics | null - validator: types.st.IValidator | null | undefined - validatorDelegations: types.st.IDelegation[] | null - sc: types.st.ISkaleContractsMap | null - loadValidator: () => Promise -}) { - const location = useLocation() - const currentUrl = `${window.location.origin}${location.pathname}${location.search}` - - const theme = useTheme() - const isXs = useMediaQuery(theme.breakpoints.down('sm')) - - const [chainsMeta, setChainsMeta] = useState(null) - const [termsAccepted, setTermsAccepted] = useState(false) - const [stakingTermsAccepted, setStakingTermsAccepted] = useState(false) - - const [validators, setValidators] = useState([]) - const [si, setSi] = useState({ 0: null, 1: null, 2: null }) - - const mpc = useMetaportStore((state: MetaportState) => state.mpc) - const transfersHistory = useMetaportStore((state) => state.transfersHistory) - const setTransfersHistory = useMetaportStore((state) => state.setTransfersHistory) - const [searchParams, _] = useSearchParams() - - const { address } = useWagmiAccount() - const { data: walletClient } = useWagmiWalletClient() - const { switchChainAsync } = useWagmiSwitchNetwork() - - useEffect(() => { - setTransfersHistory(getHistoryFromStorage(mpc.config.skaleNetwork)) - loadMetadata() - }, []) - - useEffect(() => { - props.setCustomAddress((searchParams.get('_customAddress') as types.AddressType) ?? undefined) - }, [location]) - - useEffect(() => { - if (transfersHistory.length !== 0) { - setHistoryToStorage(transfersHistory, mpc.config.skaleNetwork) - } - }, [transfersHistory]) - - async function getMainnetSigner() { - const { chainId } = await mpc.provider(constants.MAINNET_CHAIN_NAME).getNetwork() - await enforceNetwork( - chainId, - walletClient!, - switchChainAsync!, - mpc.config.skaleNetwork, - constants.MAINNET_CHAIN_NAME - ) - return walletClientToSigner(walletClient!) - } - - async function loadMetadata() { - setChainsMeta(await metadata.loadMeta(mpc.config.skaleNetwork)) - } - - async function loadValidators() { - if (!props.sc) return - const validatorsData = await getValidators(props.sc.validatorService) - setValidators(validatorsData) - } - - async function loadStakingInfo() { - if (!props.sc) return - setSi(await getStakingInfoMap(props.sc, props.customAddress ?? address)) - } - - function isToSPage(pages: any): boolean { - return pages.some( - (pathname: string) => location.pathname === pathname || location.pathname.includes(pathname) - ) - } - - if (!termsAccepted && isToSPage(BRIDGE_PAGES)) { - return ( - - ) - } - - if (!stakingTermsAccepted && isToSPage(STAKING_PAGES)) { - return ( - - ) - } - - if (!chainsMeta) - return ( -
-
-
- -
-
-

Loading SKALE Chains

-
-
-
- ) - - return ( -
- - - - - - - - - - } - /> - } /> - - } /> - - } /> - - } - /> - - - } - /> - - } - /> - - } - /> - - - } - /> - - } /> - } /> - - } /> - } /> - } /> - - - } /> - - - - } - /> - - } - /> - - } - /> - - - } - /> - - } - /> - - - - -
- ) -} diff --git a/src/pages/Chains.tsx b/src/pages/Chains.tsx index 2fef289c..0af878ad 100644 --- a/src/pages/Chains.tsx +++ b/src/pages/Chains.tsx @@ -23,10 +23,10 @@ import { Helmet } from 'react-helmet' -import { useState, useEffect } from 'react' +import { useEffect } from 'react' -import { cmn, cls, type MetaportCore } from '@skalenetwork/metaport' -import { type types, constants } from '@/core' +import { cmn, cls, useMetaportStore, useWagmiAccount } from '@skalenetwork/metaport' +import { constants } from '@/core' import Container from '@mui/material/Container' import Stack from '@mui/material/Stack' @@ -39,36 +39,39 @@ import CategoryRoundedIcon from '@mui/icons-material/CategoryRounded' import ChainsSection from '../components/chains/ChainsSection' import { META_TAGS } from '../core/meta' import SkPageInfoIcon from '../components/SkPageInfoIcon' +import usePortalStore from '../PortalStore' -export default function Chains(props: { - loadData: () => Promise - schains: types.ISChain[] - metrics: types.IMetrics | null - mpc: MetaportCore - isXs: boolean - chainsMeta: types.ChainsMetadataMap -}) { - const [_, setIntervalId] = useState() +export default function Chains() { + const { schains, chainsMeta, metrics, loadData } = usePortalStore((state) => ({ + loadData: state.loadData, + schains: state.schains, + metrics: state.metrics, + chainsMeta: state.chainsMeta + })) - const network = props.mpc.config.skaleNetwork + const { address } = useWagmiAccount() useEffect(() => { - props.loadData() - const intervalId = setInterval(props.loadData, 10000) - setIntervalId(intervalId) + loadData(mpc, address) }, []) - const appChains = props.schains.filter( + const mpc = useMetaportStore((state) => state.mpc) + const network = mpc.config.skaleNetwork + + if (!schains || schains.length === 0 || !chainsMeta) { + return
Loading chains...
+ } + + const appChains = schains.filter( (schain) => - props.chainsMeta[schain.name] && - (!props.chainsMeta[schain.name].apps || - (props.chainsMeta[schain.name].apps && - Object.keys(props.chainsMeta[schain.name].apps!).length === 1)) + chainsMeta[schain.name] && + (!chainsMeta[schain.name].apps || + (chainsMeta[schain.name].apps && Object.keys(chainsMeta[schain.name].apps!).length === 1)) ) - const otherChains = props.schains.filter((schain) => !props.chainsMeta[schain.name]) + const otherChains = schains.filter((schain) => !chainsMeta[schain.name]) - if (props.schains.length === 0) { + if (schains.length === 0) { return (
@@ -103,14 +106,14 @@ export default function Chains(props: {
- props.chainsMeta[schain.name] && - props.chainsMeta[schain.name].apps && - Object.keys(props.chainsMeta[schain.name].apps!).length > 1 + chainsMeta[schain.name] && + chainsMeta[schain.name].apps && + Object.keys(chainsMeta[schain.name].apps!).length > 1 )} - chainsMeta={props.chainsMeta} - metrics={props.metrics} + chainsMeta={chainsMeta} + metrics={metrics} skaleNetwork={network} size="lg" icon={} @@ -119,8 +122,8 @@ export default function Chains(props: { } @@ -130,8 +133,8 @@ export default function Chains(props: { } diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index da77a7fb..1b9cbb2e 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -24,8 +24,7 @@ import { useEffect } from 'react' import { Link } from 'react-router-dom' import { Container, Stack, Box, Grid, Button } from '@mui/material' -import { cmn, cls, SkPaper } from '@skalenetwork/metaport' -import { type types } from '@/core' +import { cmn, cls, SkPaper, useWagmiAccount, useMetaportStore } from '@skalenetwork/metaport' import { useApps } from '../useApps' @@ -40,26 +39,27 @@ import { SKALE_SOCIAL_LINKS } from '../core/constants' import { SECTION_ICONS, EXPLORE_CARDS } from '../components/HomeComponents' import SocialButtons from '../components/ecosystem/Socials' import UserRecommendations from '../components/ecosystem/UserRecommendations' +import usePortalStore from '../PortalStore' -interface HomeProps { - skaleNetwork: types.SkaleNetwork - chainsMeta: types.ChainsMetadataMap - metrics: types.IMetrics | null - loadData: () => Promise -} +export default function Home(): JSX.Element { + const { address } = useWagmiAccount() -export default function Home({ - skaleNetwork, - chainsMeta, - metrics, - loadData -}: HomeProps): JSX.Element { + const { chainsMeta, metrics, loadData } = usePortalStore((state) => ({ + loadData: state.loadData, + metrics: state.metrics, + chainsMeta: state.chainsMeta + })) const { newApps, trendingApps, favoriteApps, isSignedIn } = useApps(chainsMeta, metrics) + const mpc = useMetaportStore((state) => state.mpc) + const network = mpc.config.skaleNetwork + useEffect(() => { - loadData() + loadData(mpc, address) }, []) + if (!chainsMeta) return
Loading...
+ return ( @@ -76,7 +76,7 @@ export default function Home({ linkTo="/ecosystem?tab=3" component={ } /> - + (() => { + if (!chainsMeta) return [] const apps = Object.entries(chainsMeta).flatMap(([chainName, chainData]) => Object.entries(chainData.apps || {}).map(([appName, app]) => ({ chain: chainName, @@ -44,6 +48,7 @@ export function useApps(chainsMeta: types.ChainsMetadataMap, metrics: types.IMet }, [chainsMeta]) const newApps = useMemo(() => { + if (!chainsMeta) return [] const apps = getRecentApps(chainsMeta, MAX_APPS_DEFAULT) return apps.sort((a, b) => (b.added || 0) - (a.added || 0)) }, [chainsMeta])