diff --git a/src/components/common/Sidebar/cmp.tsx b/src/components/common/Sidebar/cmp.tsx index fd31063..a8788da 100644 --- a/src/components/common/Sidebar/cmp.tsx +++ b/src/components/common/Sidebar/cmp.tsx @@ -8,7 +8,7 @@ import { MouseEvent, } from 'react' import { useRouter } from 'next/router' -import { Icon, IconName } from '@aleph-front/aleph-core' +import { Icon, IconName, NotificationBadge } from '@aleph-front/aleph-core' import { StyledLogo, StyledNav1, @@ -23,65 +23,29 @@ import { StyledToggleButton, } from './styles' import { useUserStoreAllowance } from '@/hooks/common/useUserStoreAllowance' +import { useNodeIssues } from '@/hooks/common/useNodeIssues' +import { useUserStakeNodes } from '@/hooks/common/useUserStakeNodes' +import { useAppState } from '@/contexts/appState' +import { useUserNodes } from '@/hooks/common/useUserNodes' export type Route = { name?: string path: string icon?: IconName + flag?: number children?: Route[] } -const routes: Route[] = [ - { - name: 'EARN', - path: '/earn', - icon: 'earn', - children: [ - { - name: 'Staking', - path: '/earn/staking', - icon: 'earn', - }, - { - name: 'Core nodes', - path: '/earn/ccn', - icon: 'ccn', - }, - { - name: 'Compute nodes', - path: '/earn/crn', - icon: 'crn', - }, - ], - }, - { - name: 'PROFILE', - path: '/profile', - icon: 'profile', - children: [ - { - name: 'My profile', - path: '/profile', - icon: 'profile', - }, - { - name: 'Notification center', - path: '/notification', - icon: 'notification', - }, - ], - }, -] - export type SidebarLinkProps = AnchorHTMLAttributes & { href: string icon?: IconName + flag?: number isOpen?: boolean children?: ReactNode } const SidebarLink = memo( - ({ href, icon, children, isOpen, ...rest }: SidebarLinkProps) => { + ({ href, icon, children, isOpen, flag, ...rest }: SidebarLinkProps) => { const router = useRouter() const isActive = router.pathname.indexOf(href) >= 0 @@ -101,6 +65,7 @@ const SidebarLink = memo( {iconCmp} {children} + {flag && {flag}} ) }, @@ -108,6 +73,28 @@ const SidebarLink = memo( SidebarLink.displayName = 'SidebarLink' const Sidebar = memo(() => { + const [state] = useAppState() + const { entities: nodes } = state.ccns + const { entities: crns } = state.crns + + const { stakeNodes } = useUserStakeNodes({ nodes }) + const { userNodes: userCCNs } = useUserNodes({ nodes }) + const { userNodes: userCRNs } = useUserNodes({ nodes: crns }) + + const { warningFlag: stakeNodesWarningFlag } = useNodeIssues({ + nodes: stakeNodes, + }) + + const { warningFlag: userCCNsWarningFlag } = useNodeIssues({ + nodes: userCCNs, + }) + + const { warningFlag: userCRNsWarningFlag } = useNodeIssues({ + nodes: userCRNs, + }) + + // ------------------------------------------- + const [open, setOpen] = useState(undefined) const handleToggle = useCallback( @@ -128,13 +115,64 @@ const Sidebar = memo(() => { e.stopPropagation() }, []) + // ----------------------------------- + + const routes: Route[] = useMemo(() => { + return [ + { + name: 'EARN', + path: '/earn', + icon: 'earn', + children: [ + { + name: 'Staking', + path: '/earn/staking', + icon: 'earn', + flag: stakeNodesWarningFlag, + }, + { + name: 'Core nodes', + path: '/earn/ccn', + icon: 'ccn', + flag: userCCNsWarningFlag, + }, + { + name: 'Compute nodes', + path: '/earn/crn', + icon: 'crn', + flag: userCRNsWarningFlag, + }, + ], + }, + { + name: 'PROFILE', + path: '/profile', + icon: 'profile', + children: [ + { + name: 'My profile', + path: '/profile', + icon: 'profile', + }, + { + name: 'Notification center', + path: '/notification', + icon: 'notification', + }, + ], + }, + ] + }, [stakeNodesWarningFlag, userCCNsWarningFlag, userCRNsWarningFlag]) + const router = useRouter() const currentRoute = useMemo( () => routes.find((route) => router.pathname.indexOf(route.path) === 0), - [router], + [router, routes], ) + // -------------------------------------------- + const allowanceInfo = useUserStoreAllowance() const consumedSize = (allowanceInfo.consumedSize || 0) / 1024 const allowedSize = (allowanceInfo.allowedSize || 0) / 1024 @@ -182,6 +220,7 @@ const Sidebar = memo(() => { key={child.path} href={child.path} icon={child?.icon || currentRoute?.icon} + flag={child?.flag || currentRoute?.flag} > {child.name} diff --git a/src/contexts/appState.tsx b/src/contexts/appState.tsx index 3fa2b3b..cccc67e 100644 --- a/src/contexts/appState.tsx +++ b/src/contexts/appState.tsx @@ -29,7 +29,7 @@ export const AppStateContext = createContext([ export function AppStateProvider({ children }: AppStateProviderProps) { const value = useReducer(storeReducer, storeInitialState) - console.log('STORE', value[0]) + // console.log('STORE', value[0]) return ( diff --git a/src/hooks/common/useUserNodes.ts b/src/hooks/common/useUserNodes.ts new file mode 100644 index 0000000..1aaaf27 --- /dev/null +++ b/src/hooks/common/useUserNodes.ts @@ -0,0 +1,36 @@ +import { useCallback, useMemo } from 'react' +import { CCN, CRN, NodeManager } from '@/domain/node' +import { useAppState } from '@/contexts/appState' + +export type UseUserNodesProps = { + nodes?: Node[] +} + +export type UseUserNodesReturn = { + userNodes?: Node[] +} + +export function useUserNodes({ + nodes, +}: UseUserNodesProps): UseUserNodesReturn { + const [state] = useAppState() + const { account } = state.account + + // @todo: Refactor this (use singleton) + const nodeManager = useMemo(() => new NodeManager(account), [account]) + + const filterUserNodes = useCallback( + (nodes?: Node[]) => { + if (!nodes) return + return nodes.filter((node) => nodeManager.isUserNode(node)) + }, + [nodeManager], + ) + + const userNodes = useMemo( + () => filterUserNodes(nodes), + [filterUserNodes, nodes], + ) + + return { userNodes } +} diff --git a/src/hooks/common/useUserStakeNodes.ts b/src/hooks/common/useUserStakeNodes.ts new file mode 100644 index 0000000..a477c9b --- /dev/null +++ b/src/hooks/common/useUserStakeNodes.ts @@ -0,0 +1,36 @@ +import { useCallback, useMemo } from 'react' +import { CCN, NodeManager } from '@/domain/node' +import { useAppState } from '@/contexts/appState' + +export type UseUserStakeNodesProps = { + nodes?: CCN[] +} + +export type UseUserStakeNodesReturn = { + stakeNodes?: CCN[] +} + +export function useUserStakeNodes({ + nodes, +}: UseUserStakeNodesProps): UseUserStakeNodesReturn { + const [state] = useAppState() + const { account } = state.account + + // @todo: Refactor this (use singleton) + const nodeManager = useMemo(() => new NodeManager(account), [account]) + + const filterStakeNodes = useCallback( + (nodes?: CCN[]) => { + if (!nodes) return + return nodes.filter((node) => nodeManager.isUserStake(node)) + }, + [nodeManager], + ) + + const stakeNodes = useMemo( + () => filterStakeNodes(nodes), + [filterStakeNodes, nodes], + ) + + return { stakeNodes } +} diff --git a/src/hooks/pages/earn/useComputeResourceNodesPage.tsx b/src/hooks/pages/earn/useComputeResourceNodesPage.tsx index 19c4689..c5be46c 100644 --- a/src/hooks/pages/earn/useComputeResourceNodesPage.tsx +++ b/src/hooks/pages/earn/useComputeResourceNodesPage.tsx @@ -1,13 +1,14 @@ -import { useCallback, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { NotificationBadge, TabsProps } from '@aleph-front/aleph-core' import { useAppState } from '@/contexts/appState' -import { CRN, NodeManager } from '@/domain/node' +import { CRN } from '@/domain/node' import { UseComputeResourceNodesReturn, useComputeResourceNodes, } from '@/hooks/common/useComputeResourceNodes' import { useNodeIssues } from '@/hooks/common/useNodeIssues' import { useLastRewards } from '@/hooks/common/useRewards' +import { useUserNodes } from '@/hooks/common/useUserNodes' export type UseComputeResourceNodesPageProps = { nodes?: CRN[] @@ -33,30 +34,14 @@ export function useComputeResourceNodesPage( // ----------------------------- - // @todo: Refactor this (use singleton) - const nodeManager = useMemo(() => new NodeManager(account), [account]) - const { nodes, filteredNodes, ...rest } = useComputeResourceNodes(props) // ----------------------------- - const filterUserNodes = useCallback( - (nodes?: CRN[]) => { - if (!nodes) return - return nodes.filter((node) => nodeManager.isUserNode(node)) - }, - [nodeManager], - ) - - const userNodes = useMemo( - () => filterUserNodes(nodes), - [filterUserNodes, nodes], - ) - - const filteredUserNodes = useMemo( - () => filterUserNodes(filteredNodes), - [filterUserNodes, filteredNodes], - ) + const { userNodes } = useUserNodes({ nodes }) + const { userNodes: filteredUserNodes } = useUserNodes({ + nodes: filteredNodes, + }) // ----------------------------- diff --git a/src/hooks/pages/earn/useCoreChannelNodesPage.tsx b/src/hooks/pages/earn/useCoreChannelNodesPage.tsx index 7464551..3d6b2e6 100644 --- a/src/hooks/pages/earn/useCoreChannelNodesPage.tsx +++ b/src/hooks/pages/earn/useCoreChannelNodesPage.tsx @@ -1,13 +1,14 @@ -import { useCallback, useMemo, useState } from 'react' +import { useMemo, useState } from 'react' import { NotificationBadge, TabsProps } from '@aleph-front/aleph-core' import { useAppState } from '@/contexts/appState' -import { CCN, NodeManager } from '@/domain/node' +import { CCN } from '@/domain/node' import { UseCoreChannelNodesReturn, useCoreChannelNodes, } from '@/hooks/common/useCoreChannelNodes' import { useNodeIssues } from '@/hooks/common/useNodeIssues' import { useLastRewards } from '@/hooks/common/useRewards' +import { useUserNodes } from '@/hooks/common/useUserNodes' export type UseCoreChannelNodesPageProps = { nodes?: CCN[] @@ -30,30 +31,14 @@ export function useCoreChannelNodesPage( const [state] = useAppState() const { account, balance: accountBalance = 0 } = state.account - // @todo: Refactor this (use singleton) - const nodeManager = useMemo(() => new NodeManager(account), [account]) - const { nodes, filteredNodes, ...rest } = useCoreChannelNodes(props) // ----------------------------- - const filterUserNodes = useCallback( - (nodes?: CCN[]) => { - if (!nodes) return - return nodes.filter((node) => nodeManager.isUserNode(node)) - }, - [nodeManager], - ) - - const userNodes = useMemo( - () => filterUserNodes(nodes), - [filterUserNodes, nodes], - ) - - const filteredUserNodes = useMemo( - () => filterUserNodes(filteredNodes), - [filterUserNodes, filteredNodes], - ) + const { userNodes } = useUserNodes({ nodes }) + const { userNodes: filteredUserNodes } = useUserNodes({ + nodes: filteredNodes, + }) // ----------------------------- diff --git a/src/hooks/pages/earn/useStakingPage.tsx b/src/hooks/pages/earn/useStakingPage.tsx index 28bd5a3..e440b4f 100644 --- a/src/hooks/pages/earn/useStakingPage.tsx +++ b/src/hooks/pages/earn/useStakingPage.tsx @@ -7,6 +7,7 @@ import { } from '@/hooks/common/useCoreChannelNodes' import { useNodeIssues } from '@/hooks/common/useNodeIssues' import { useAccountRewards } from '@/hooks/common/useRewards' +import { useUserStakeNodes } from '@/hooks/common/useUserStakeNodes' import { NotificationBadge, TabsProps } from '@aleph-front/aleph-core' import { ChangeEvent, useCallback, useMemo, useState } from 'react' @@ -68,23 +69,10 @@ export function useStakingPage( // ----------------------------- - const filterStakeNodes = useCallback( - (nodes?: CCN[]) => { - if (!nodes) return - return nodes.filter((node) => nodeManager.isUserStake(node)) - }, - [nodeManager], - ) - - const stakeNodes = useMemo( - () => filterStakeNodes(nodes), - [filterStakeNodes, nodes], - ) - - const filteredStakeNodes = useMemo( - () => filterStakeNodes(baseFilteredNodes), - [filterStakeNodes, baseFilteredNodes], - ) + const { stakeNodes } = useUserStakeNodes({ nodes }) + const { stakeNodes: filteredStakeNodes } = useUserStakeNodes({ + nodes: baseFilteredNodes, + }) const filteredNodes = useMemo(() => { if (!baseFilteredNodes) return