import React, { useEffect, useState } from 'react'; import { ToastContainer, toast } from 'react-toastify'; import ReactTooltip from 'react-tooltip'; import { Role, NetworkURL, Network as NetworkLabel, AccountType, Environment, Constants, TransactionType, ButtonText, ContractState, OperationStatus } from '../util/enum'; import { convertQaToCommaStr, getAddressLink } from '../util/utils'; import StakingPortfolio from './staking-portfolio'; import SsnTable from './ssn-table'; import Alert from './alert'; import { toBech32Address } from '@zilliqa-js/crypto'; import WithdrawCommModal from './contract-calls/withdraw-comm'; import UpdateReceiverAddress from './contract-calls/update-receiver-address'; import UpdateCommRateModal from './contract-calls/update-commission-rate'; import DelegateStakeModal from './contract-calls/delegate-stake'; import ReDelegateStakeModal from './contract-calls/redeleg'; import WithdrawStakeModal from './contract-calls/withdraw-stake'; import WithdrawRewardModal from './contract-calls/withdraw-reward'; import CompleteWithdrawModal from './contract-calls/complete-withdraw'; import SwapDelegModal from './contract-calls/swap-deleg'; import logo from "../static/logo.png"; import DisclaimerModal from './disclaimer'; import DelegatorStatsTable from './delegator-stats-table'; import OperatorStatsTable from './operator-stats-table'; import CompleteWithdrawalTable from './complete-withdrawal-table'; import IconShuffle from './icons/shuffle'; import IconQuestionCircle from './icons/question-circle'; import IconRefresh from './icons/refresh'; import IconBell from './icons/bell'; import IconCheckboxBlankCircle from './icons/checkbox-blank-circle'; import IconSun from './icons/sun'; import IconMoon from './icons/moon'; import useDarkMode from '../util/use-dark-mode'; import { getLocalItem, storeLocalItem } from '../util/use-local-storage'; import Footer from './footer'; import RecentTxnDropdown from './recent-txn'; import Tippy from '@tippyjs/react'; import '../tippy.css'; import 'tippy.js/animations/shift-away-subtle.css'; import WarningDashboardBanner from './warning-dashboard-banner'; import { POLL_USER_DATA_STOP, QUERY_AND_UPDATE_USER_STATS, RESET_USER_STATE, UPDATE_ADDRESS } from '../store/userSlice'; import { useAppDispatch, useAppSelector } from '../store/hooks'; import { logger } from '../util/logger'; import { getEnvironment, getNetworks, NetworkConfig, Networks } from '../util/config-json-helper'; import { RESET_BLOCKCHAIN_STATE, UPDATE_CHAIN_INFO } from '../store/blockchainSlice'; import { ZilSigner } from '../zilliqa-signer'; import { QUERY_AND_UPDATE_STAKING_STATS } from '../store/stakingSlice'; function Dashboard(props: any) { const dispatch = useAppDispatch(); const userState = useAppSelector(state => state.user); const isDelegStatsLoading = useAppSelector(state => state.user.is_deleg_stats_loading); const blockchainState = useAppSelector(state => state.blockchain); // config.js from public folder const env = getEnvironment(); const networks: Networks = getNetworks(); const proxy = useAppSelector(state => state.blockchain.proxy); const networkURL = useAppSelector(state => state.blockchain.blockchain); const [walletAddress, setWalletAddress] = useState(userState.address_base16 || ''); const [blockchain, setBlockchain] = useState(blockchainState.blockchain || ''); const [isRefreshDisabled, setIsRefreshDisabled] = useState(false); const [isTxnNotify, setIsTxnNotify] = useState(false); const [ariaExpanded, setAriaExpanded] = useState(false); const [recentTransactions, setRecentTransactions] = useState([] as any) const darkMode = useDarkMode(true); const cleanUp = () => { logger("directing to main"); dispatch(POLL_USER_DATA_STOP()); dispatch(RESET_USER_STATE()); dispatch(RESET_BLOCKCHAIN_STATE()); props.history.push("/"); } // set recent txn indicator icon const handleTxnNotify = () => { if (!isTxnNotify) { return; } setIsTxnNotify(false); } const toggleTheme = () => { if (darkMode.value === true) { darkMode.disable(); } else { darkMode.enable(); } } const updateRecentTransactions = (type: TransactionType, txnId: string) => { let temp = JSON.parse(JSON.stringify(recentTransactions)); if ((temp.length + 1) > 10) { // suppose we add a new element // restrict number of elements as local storage has limits // recent txn is always in newest to oldest // remove last element - last element = oldest txn temp.pop(); } // reverse so that order is oldest to newest // add new item as last element temp = temp.reverse(); temp.push({type: type, txnId: txnId}); // restore order back setRecentTransactions([...temp].reverse()); storeLocalItem(userState.address_bech32, proxy, networkURL, 'recent-txn', temp.reverse()); // set recent txn indicator icon setIsTxnNotify(true); } // re-hydrate data from localstorage useEffect(() => { let txns = getLocalItem(userState.address_bech32, proxy, networkURL, 'recent-txn', [] as any); setRecentTransactions(txns); }, [userState.address_bech32, proxy, networkURL]); const timeout = (delay: number) => { return new Promise(res => setTimeout(res, delay)); } const pollData = async () => { console.log("polling data...") setIsRefreshDisabled(true); dispatch(QUERY_AND_UPDATE_USER_STATS()); dispatch(QUERY_AND_UPDATE_STAKING_STATS()); await timeout(Constants.MANUAL_REFRESH_DELAY); setIsRefreshDisabled(false); } // for zilpay to toggle different network const networkChanger = (net: string) => { let label; switch (net) { case NetworkLabel.MAINNET: // do nothing Alert("info", "Info", "You are on Mainnet."); label = NetworkLabel.MAINNET; break; case NetworkLabel.TESTNET: label = NetworkLabel.TESTNET; if (env === Environment.PROD) { // warn users not to switch to testnet on production Alert("warn", "Testnet not supported", "Please switch to Mainnet via ZilPay."); } break; case NetworkLabel.ISOLATED_SERVER: case NetworkLabel.PRIVATE: label = NetworkLabel.ISOLATED_SERVER; if (env === Environment.PROD) { // warn users not to switch to testnet on production Alert("warn", "Private network not supported", "Please switch to Mainnet via ZilPay."); } break; default: label = NetworkLabel.TESTNET; break; } const networkConfig: NetworkConfig = networks[label]; dispatch(UPDATE_CHAIN_INFO({ proxy: networkConfig.proxy || '', impl: networkConfig.impl || '', blockchain: networkConfig.blockchain || '', staking_viewer: networkConfig.node_status || '', api_list: networkConfig.api_list || [], })); } /** * When document has loaded, it start to observable network form zilpay. */ useEffect(() => { if (userState.account_type === AccountType.ZILPAY) { const zilPay = (window as any).zilPay; if (zilPay) { // switch to the zilpay network on load networkChanger(zilPay.wallet.net); const accountStreamChanged = zilPay.wallet.observableAccount().subscribe((account: any) => { console.log("zil pay account changing..."); const bech32 = toBech32Address(account.base16); dispatch(UPDATE_ADDRESS({ address_base16: account.base16, address_bech32: bech32 })); }); const networkStreamChanged = zilPay.wallet.observableNetwork().subscribe((net: string) => networkChanger(net)); return () => { accountStreamChanged.unsubscribe(); networkStreamChanged.unsubscribe(); }; } } // must only run once due to global listener // eslint-disable-next-line }, []); useEffect(() => { if (env === Environment.DEV) { // disable auth check for development return; } if (!userState.authenticated) { // redirect to login request dispatch(POLL_USER_DATA_STOP()); dispatch(RESET_USER_STATE()); dispatch(RESET_BLOCKCHAIN_STATE()); props.history.push("/oops"); } }, [env, userState.authenticated, props.history, dispatch]); // change to correct role useEffect(() => { console.log("change wallet") if (walletAddress !== userState.address_base16) { // address changed dispatch(QUERY_AND_UPDATE_USER_STATS()); } setWalletAddress(userState.address_base16); }, [walletAddress, userState.address_base16, dispatch]); useEffect(() => { console.log("change network") if (blockchain !== blockchainState.blockchain) { // network changed dispatch(QUERY_AND_UPDATE_USER_STATS()); dispatch(QUERY_AND_UPDATE_STAKING_STATS()); ZilSigner.changeNetwork(blockchainState.blockchain); } setBlockchain(blockchainState.blockchain); }, [blockchain, blockchainState.blockchain, dispatch]); // prevent user from refreshing useEffect(() => { window.onbeforeunload = (e: any) => { e.preventDefault(); e.returnValue = 'The page auto retrieves data periodically. Please do not force refresh as you will lose your wallet connection.'; setTimeout(() => { toast.dismiss(); }, 8000); return ( Alert("warn", "Warning", "The app auto retrieves data periodically. Please do not force refresh as you will lose your wallet connection.") ); } }, []); useEffect(() => { if (userState.selected_role === Role.OPERATOR && userState.role === Role.DELEGATOR) { Alert("warn", "Warning", "You have been redirected to the delegator dashboard."); } }, [userState.selected_role, userState.role]); // eslint-disable-next-line return ( <>
Refresh
{/* delegator section */} {/* complete withdrawal */} { (userState.role === Role.DELEGATOR) && } { (userState.role === Role.OPERATOR) && <> {/* node operator section */}
Hi {userState.operator_stats.name ? userState.operator_stats.name : 'Operator'}! What would you like to do today?
} { (userState.role === Role.DELEGATOR) && <> {/* delegator statistics */}
Overview
} { (userState.role === Role.DELEGATOR) && <> {/* delegator portfolio */}
My Staking Portfolio
Deposits
This shows you the list of nodes which you have staked your deposit in.
} {/* operator statistics */} { (userState.role === Role.OPERATOR) &&
My Node Performance
}
Staked Seed Nodes

Please refer to our  Staking Viewer  for more information on the nodes' statuses.

); } export default Dashboard;