From 7ca2559f99760d27f617c5296ff677e5042ec44a Mon Sep 17 00:00:00 2001 From: Yana Date: Thu, 20 Mar 2025 20:09:40 +0200 Subject: [PATCH] Add caching on tanstack queries clean up Another try clean up fix build fix build fix build fix build Refactor Node page to accept identity_key in params fix build fix build fix buggy data on landing page graphs Try fix gas fee for redeem all rewards Another try to fix gas fee for redeem rewards Add fees "auto" to the cosmWasm client with offline signer comment out unused option add getOfflineSigner dependency to the callback fn comment out for good clean up, optimise homepage layout Dark theme fix build fix build add fixes Rebase onto develop, fix lint error fix build Fix tooltip Fix switch button on mobile header fix build clean up fix build Fix switch component fix build Add moniker to Magic Search, fix tooltip hover on landing page refactor urls fix build edit placeholder Fix styles fix error message --- explorer-v2/src/app/api/urls.ts | 8 +- .../copyToClipboard/CopyToClipboard.tsx | 10 +- .../src/components/header/DesktopHeader.tsx | 2 +- .../src/components/header/MobileHeader.tsx | 2 +- explorer-v2/src/components/header/Switch.tsx | 17 +- .../landingPageComponents/NoiseCard.tsx | 26 ++- .../nymNodePageComponents/BasicInfoCard.tsx | 2 +- .../search/NodeAndAddressSearch.tsx | 198 ++++++++++++++---- 8 files changed, 205 insertions(+), 60 deletions(-) diff --git a/explorer-v2/src/app/api/urls.ts b/explorer-v2/src/app/api/urls.ts index c1cd2ede91..f6004e5604 100644 --- a/explorer-v2/src/app/api/urls.ts +++ b/explorer-v2/src/app/api/urls.ts @@ -2,8 +2,7 @@ export const CURRENT_EPOCH = "https://validator.nymtech.net/api/v1/epoch/current"; export const CURRENT_EPOCH_REWARDS = "https://validator.nymtech.net/api/v1/epoch/reward_params"; -export const NYM_NODE_BONDED = - "https://validator.nymtech.net/api/v1/nym-nodes/bonded"; + export const NYM_ACCOUNT_ADDRESS = "https://explorer.nymtech.net/api/v1/tmp/unstable/account"; export const NYM_PRICES_API = "https://api.nym.spectredao.net/api/v1/nym-price"; @@ -18,3 +17,8 @@ export const DATA_OBSERVATORY_BALANCES_URL = "https://api.nym.spectredao.net/api/v1/balances"; export const OBSERVATORY_GATEWAYS_URL = "https://mainnet-node-status-api.nymtech.cc/v2/gateways"; + +export const NS_API_MIXNODES_STATS = + process.env.NEXT_PUBLIC_NS_API_MIXNODES_STATS; + +export const NS_API_NODES = process.env.NEXT_PUBLIC_NS_API_NODES; diff --git a/explorer-v2/src/components/copyToClipboard/CopyToClipboard.tsx b/explorer-v2/src/components/copyToClipboard/CopyToClipboard.tsx index 841bf21ff3..fe86fdf98c 100644 --- a/explorer-v2/src/components/copyToClipboard/CopyToClipboard.tsx +++ b/explorer-v2/src/components/copyToClipboard/CopyToClipboard.tsx @@ -20,6 +20,7 @@ const CopyToClipboard = ({ const [copiedText, setCopiedText] = useCopyToClipboard(); const hasCopied = Boolean(copiedText); const theme = useTheme(); + const isDarkMode = theme.palette.mode === "dark"; useEffect(() => { if (hasCopied) { @@ -33,7 +34,11 @@ const CopyToClipboard = ({ if (hasCopied) { return ( - + Copied ); @@ -41,8 +46,7 @@ const CopyToClipboard = ({ return ( setCopiedText(text)}> - {Icon || - (theme.palette.mode === "dark" ? : )} + {Icon || (isDarkMode ? : )} ); }; diff --git a/explorer-v2/src/components/header/DesktopHeader.tsx b/explorer-v2/src/components/header/DesktopHeader.tsx index 548fd564f0..94a1a72227 100644 --- a/explorer-v2/src/components/header/DesktopHeader.tsx +++ b/explorer-v2/src/components/header/DesktopHeader.tsx @@ -51,7 +51,7 @@ export const DesktopHeader = () => { ))} - + diff --git a/explorer-v2/src/components/header/MobileHeader.tsx b/explorer-v2/src/components/header/MobileHeader.tsx index d0fb98cd0a..b43c7d6852 100644 --- a/explorer-v2/src/components/header/MobileHeader.tsx +++ b/explorer-v2/src/components/header/MobileHeader.tsx @@ -162,7 +162,7 @@ const MobileMenuHeader = ({ alignItems: "center", }} > - {!drawerOpen && } + {!drawerOpen && } toggleDrawer(!drawerOpen)}> {drawerOpen ? : } diff --git a/explorer-v2/src/components/header/Switch.tsx b/explorer-v2/src/components/header/Switch.tsx index ff40a40fb6..4e967d1c7e 100644 --- a/explorer-v2/src/components/header/Switch.tsx +++ b/explorer-v2/src/components/header/Switch.tsx @@ -52,27 +52,14 @@ export const DarkLightSwitch = styled(Switch)(({ theme }) => ({ }, })); -// export const DarkLightSwitchMobile: FCWithChildren = () => { -// const { toggleMode } = useMainContext(); -// return ( -// -// ); -// }; - -export const DarkLightSwitchDesktop: FCWithChildren<{ - defaultChecked: boolean; -}> = ({ defaultChecked }) => { +export const DarkLightSwitchDesktop = (): JSX.Element => { const [mode, setMode] = useLocalStorage("mode", "dark"); - console.log("mode", mode); - const toggleMode = () => setMode((m) => (m !== "light" ? "light" : "dark")); return ( ); }; diff --git a/explorer-v2/src/components/landingPageComponents/NoiseCard.tsx b/explorer-v2/src/components/landingPageComponents/NoiseCard.tsx index fdd3786404..51d218c263 100644 --- a/explorer-v2/src/components/landingPageComponents/NoiseCard.tsx +++ b/explorer-v2/src/components/landingPageComponents/NoiseCard.tsx @@ -10,6 +10,7 @@ import { useTheme, } from "@mui/material"; import { useQuery } from "@tanstack/react-query"; +import { useState } from "react"; import type { IPacketsAndStakingData } from "../../app/api/types"; import { formatBigNum } from "../../utils/formatBigNumbers"; import ExplorerCard from "../cards/ExplorerCard"; @@ -19,6 +20,8 @@ import { UpDownPriceIndicator } from "../price/UpDownPriceIndicator"; export const NoiseCard = () => { const theme = useTheme(); const isDarkMode = theme.palette.mode === "dark"; + const [tooltipOpen, setTooltipOpen] = useState(false); + const { data, isLoading, isError } = useQuery({ queryKey: ["noise"], queryFn: fetchNoise, @@ -110,6 +113,14 @@ export const NoiseCard = () => { }) .filter((item) => item.numericData >= 2_500_000_000); + const handleTooltipOpen = () => { + setTooltipOpen(true); + }; + + const handleTooltipClose = () => { + setTooltipOpen(false); + }; + return ( @@ -124,11 +135,22 @@ export const NoiseCard = () => { {noiseLast24HFormatted} e.stopPropagation()} + enterNextDelay={300} + leaveDelay={200} > - + ({formatedNoiseVolume}) diff --git a/explorer-v2/src/components/nymNodePageComponents/BasicInfoCard.tsx b/explorer-v2/src/components/nymNodePageComponents/BasicInfoCard.tsx index 6c46d53a44..7cae2ed050 100644 --- a/explorer-v2/src/components/nymNodePageComponents/BasicInfoCard.tsx +++ b/explorer-v2/src/components/nymNodePageComponents/BasicInfoCard.tsx @@ -68,7 +68,7 @@ export const BasicInfoCard = ({ paramId }: Props) => { const selfBondFormatted = `${selfBond} NYM`; return ( - + { const router = useRouter(); const [inputValue, setInputValue] = useState(""); const [errorText, setErrorText] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [searchOptions, setSearchOptions] = useState([]); + + // Use React Query to fetch nodes + const { data: nymNodes = [], isLoading: isLoadingNodes } = useQuery({ + queryKey: ["nymNodes"], + queryFn: fetchObservatoryNodes, + staleTime: 10 * 60 * 1000, // 10 minutes + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); const handleSearch = async () => { setErrorText(""); // Clear any previous error messages @@ -20,9 +39,7 @@ const NodeAndAddressSearch = () => { try { if (inputValue.startsWith("n1")) { // Fetch Nym Address data - const response = await fetch( - `https://explorer.nymtech.net/api/v1/tmp/unstable/account/${inputValue}`, - ); + const response = await fetch(`${NYM_ACCOUNT_ADDRESS}/${inputValue}`); if (response.ok) { try { @@ -33,7 +50,7 @@ const NodeAndAddressSearch = () => { } } catch { setErrorText( - "It seems that this node or account does not exist. Please enter a complete Node ID or an existing Nym wallet address.", + "No node found with the provided Name, Node ID or Identity Key. Please check your input and try again." ); setIsLoading(false); // Stop loading @@ -41,56 +58,156 @@ const NodeAndAddressSearch = () => { } } else { setErrorText( - "It seems that this node or account does not exist. Please enter a complete Node ID or an existing Nym wallet address.", + "No node found with the provided Name, Node ID or Identity Key. Please check your input and try again." ); setIsLoading(false); // Stop loading return; } } else { - // Fetch Nym Nodes data - const response = await fetch(NYM_NODE_BONDED); - - if (response.ok) { - const nodes = await response.json(); - const matchingNode = nodes.data.find( - (node: NodeData) => - node.bond_information.node.identity_key === inputValue, + // Check if it's a node identity key + if (nymNodes) { + const matchingNode = nymNodes.find( + (node) => node.identity_key === inputValue ); if (matchingNode) { - router.push(`/nym-node/${matchingNode.bond_information.node_id}`); + router.push(`/nym-node/${matchingNode.identity_key}`); return; } } setErrorText( - "It seems that this node or account does not exist. Please enter a complete Node ID or an existing Nym wallet address.", + "No node found with the provided Name, Node ID or Identity Key. Please check your input and try again." ); - setIsLoading(false); // Stop loading + setIsLoading(false); } } catch (error) { setErrorText( - "It seems that this node or account does not exist. Please enter a complete Node ID or an existing Nym wallet address.", + "No node found with the provided Name, Node ID or Identity Key. Please check your input and try again." ); console.error(error); setIsLoading(false); // Stop loading } }; + // Handle search input change + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const value = event.target.value; + setInputValue(value); + + // Clear error message when input is empty + if (!value.trim()) { + setErrorText(""); + } + + // Filter nodes by moniker if input is not empty + if (value.trim() !== "") { + const filteredNodes = nymNodes.filter((node) => + node.self_description?.moniker + ?.toLowerCase() + .includes(value.toLowerCase()) + ); + setSearchOptions(filteredNodes); + } else { + setSearchOptions([]); + } + }; + + // Handle node selection from dropdown + const handleNodeSelect = ( + event: React.SyntheticEvent, + value: string | IObservatoryNode | null + ) => { + if (value && typeof value !== "string") { + setIsLoading(true); // Show loading spinner + router.push(`/nym-node/${value.node_id}`); + } + }; + return ( - + - setInputValue(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - handleSearch(); - } + { + if (typeof option === "string") return option; + return option.self_description?.moniker || ""; + }} + isOptionEqualToValue={(option, value) => { + if (typeof option === "string" || typeof value === "string") + return false; + return option.node_id === value.node_id; + }} + renderOption={(props, option) => { + if (typeof option === "string") return null; + return ( +
  • + {option.self_description?.moniker || "Unnamed Node"} +
  • + ); + }} + renderInput={(params) => ( + { + if (e.key === "Enter") { + handleSearch(); + } + }} + sx={{ + "& .MuiOutlinedInput-root": { + borderRadius: "28px", + backgroundColor: "background.paper", + "& fieldset": { + borderColor: "divider", + }, + "&:hover fieldset": { + borderColor: "primary.main", + }, + "&.Mui-focused fieldset": { + borderColor: "primary.main", + }, + }, + }} + /> + )} + onChange={handleNodeSelect} + loading={isLoadingNodes} + loadingText="Loading nodes..." + noOptionsText="No nodes found" + slotProps={{ + paper: { + sx: { + marginTop: "4px", + marginLeft: "10px", + marginRight: "10px", + width: "calc(100% - 20px)", + borderRadius: "10px", + "& .MuiAutocomplete-listbox": { + padding: "8px 0", + }, + }, + }, + }} + sx={{ + flexGrow: 1, + "& .MuiAutocomplete-popupIndicator": { + color: "text.secondary", + }, + "& .MuiAutocomplete-clearIndicator": { + color: "text.secondary", + }, }} - rounded />
    {errorText && ( - + {errorText} )}