Add basepath on environment change

This commit is contained in:
Yana
2025-06-24 15:26:58 +03:00
parent d0b530e52d
commit 60ce7a4d58
26 changed files with 326 additions and 179 deletions
+88
View File
@@ -0,0 +1,88 @@
import { type Environment } from "../src/providers/EnvironmentProvider";
interface EnvConfig {
envName: Environment;
basePath: string;
apiUrl?: string;
}
function log(message?: any, ...optionalParams: any[]) {
if (
process.env.NODE_ENV === "development" ||
process.env.DEBUG_CONFIG_LOGS === "true"
) {
console.log(message, ...optionalParams);
}
}
// export function getCurrentEnv(): Environment {
// // Check for VERCEL_ENV from .env file
// if (process.env.VERCEL_ENV === "sandbox") {
// return "sandbox";
// }
// if (process.env.VERCEL_ENV === "production") {
// return "mainnet";
// }
// // Check for environment-specific deployment branches
// if (process.env.VERCEL_GIT_COMMIT_REF === "deploy/sandbox") {
// return "sandbox";
// }
// if (process.env.VERCEL_GIT_COMMIT_REF === "deploy/mainnet") {
// return "mainnet";
// }
// // Check for NODE_ENV
// if (process.env.NODE_ENV === "production") {
// return "mainnet";
// }
// // Default to mainnet for development
// return "mainnet";
// }
function getMainnetEnv(): EnvConfig {
return {
envName: "mainnet",
basePath: "/explorer",
// apiUrl:
// process.env.NEXT_PUBLIC_MAINNET_API_URL || "https://nym.com/explorer",
};
}
function getSandboxEnv(): EnvConfig {
return {
envName: "sandbox",
basePath: "/sandbox-explorer",
// apiUrl:
// process.env.NEXT_PUBLIC_SANDBOX_API_URL ||
// "https://nym.com/sandbox-explorer",
};
}
export const getEnvByName = (name: Environment): EnvConfig => {
if (name === "sandbox") {
return getSandboxEnv();
}
if (name === "mainnet") {
return getMainnetEnv();
}
// Default to mainnet
log("🐼 using mainnet env vars");
return getMainnetEnv();
};
// export const getEnv = (): EnvConfig => {
// const currentEnv = getCurrentEnv();
// log(`currentEnv = "${currentEnv}"`);
// return getEnvByName(currentEnv);
// };
export const getBasePathByEnv = (env: Environment): string => {
return getEnvByName(env).basePath;
};
// export const getApiUrlByEnv = (env: Environment): string => {
// return getEnvByName(env).apiUrl;
// };
+19 -9
View File
@@ -2,19 +2,29 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
basePath: "/explorer",
assetPrefix: "/explorer",
trailingSlash: false,
async redirects() {
async rewrites() {
return [
// Change the basePath to /explorer
// Rewrite /sandbox-explorer to root
{
source: "/",
destination: "/explorer",
basePath: false,
permanent: true,
source: "/sandbox-explorer",
destination: "/",
},
// Rewrite /explorer to root
{
source: "/explorer",
destination: "/",
},
// Rewrite /sandbox-explorer/* to /*
{
source: "/sandbox-explorer/:path*",
destination: "/:path*",
},
// Rewrite /explorer/* to /*
{
source: "/explorer/:path*",
destination: "/:path*",
},
];
},
@@ -0,0 +1,34 @@
"use client";
import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../../../../envs/config";
interface AccountNotFoundClientProps {
address: string;
}
export default function AccountNotFoundClient({
address,
}: AccountNotFoundClientProps) {
const { environment } = useEnvironment();
const basePath = getBasePathByEnv(environment || "mainnet");
return (
<ExplorerButtonGroup
onPage="Account"
options={[
{
label: "Nym Node",
isSelected: true,
link: `${basePath}/account/${address}/not-found/`,
},
{
label: "Account",
isSelected: false,
link: `${basePath}/account/${address}`,
},
]}
/>
);
}
@@ -1,62 +1,33 @@
// import BlogArticlesCards from "@/components/blogs/BlogArticleCards";
import { ContentLayout } from "@/components/contentLayout/ContentLayout";
import SectionHeading from "@/components/headings/SectionHeading";
import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton";
import { Box, Typography } from "@mui/material";
import Grid from "@mui/material/Grid2";
import Markdown from "react-markdown";
import AccountNotFoundClient from "./AccountNotFoundClient";
export default async function Account({
export default async function AccountNotFound({
params,
}: {
params: Promise<{ address: string }>;
}) {
try {
const address = (await params).address;
const { address } = await params;
return (
<ContentLayout>
<Grid container columnSpacing={5} rowSpacing={5}>
<Grid size={12}>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<SectionHeading title="Nym Node Details" />
<ExplorerButtonGroup
onPage="Account"
options={[
{
label: "Nym Node",
isSelected: true,
link: `/account/${address}/not-found/`,
},
{
label: "Account",
isSelected: false,
link: `/account/${address}`,
},
]}
/>
</Box>
</Grid>
return (
<ContentLayout>
<Grid container columnSpacing={5} rowSpacing={5}>
<Grid size={12}>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<SectionHeading title="Nym Node Details" />
<AccountNotFoundClient address={address} />
</Box>
</Grid>
<Typography variant="h5">
<Markdown className="reactMarkDownLink">
This account doest have a Nym node bonded. Is this your account?
Start [setting up your node](https://nym.com/docs) today!
</Markdown>
</Typography>
{/* <Grid container columnSpacing={5} rowSpacing={5}>
<Grid size={12}>
<SectionHeading title="Onboarding" />
</Grid>
<BlogArticlesCards ids={[1]} />
</Grid> */}
</ContentLayout>
);
} catch (error) {
let errorMessage = "An error occurred";
if (error instanceof Error) {
errorMessage = error.message;
}
throw new Error(errorMessage);
}
</Grid>
<Typography variant="h5">
<Markdown className="reactMarkDownLink">
This account does't have a Nym node bonded. Is this your account?
Start [setting up your node](https://nym.com/docs) today!
</Markdown>
</Typography>
</ContentLayout>
);
}
@@ -81,17 +81,18 @@ export const AccountBalancesCard = (props: IAccountBalancesCardProps) => {
const {
data: nymPrice,
isLoading: isLoadingPrice,
error: priceError,
isLoading: isNymPriceLoading,
isError: isNymPriceError,
} = useQuery({
queryKey: ["nymPrice"],
queryFn: fetchNymPrice,
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
refetchOnReconnect: false,
refetchOnMount: false,
});
if (isLoading || isLoadingPrice) {
if (isLoading || isNymPriceLoading) {
return (
<ExplorerCard label="Total value">
<Stack gap={1}>
@@ -102,7 +103,7 @@ export const AccountBalancesCard = (props: IAccountBalancesCardProps) => {
);
}
if (isError || priceError || !accountInfo || !nymPrice) {
if (isError || isNymPriceError || !accountInfo || !nymPrice) {
return (
<ExplorerCard label="Total value">
<Typography
@@ -5,6 +5,8 @@ import type { NS_NODE } from "@/app/api/types";
import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { useQuery } from "@tanstack/react-query";
import { getBasePathByEnv } from "../../../envs/config";
import { Box } from "@mui/material";
type Props = {
address: string;
@@ -12,6 +14,7 @@ type Props = {
export default function AccountPageButtonGroup({ address }: Props) {
const { environment } = useEnvironment();
const basePath = getBasePathByEnv(environment || "mainnet");
const { data: nsApiNodes = [], isError: isNSApiNodesError } = useQuery({
queryKey: ["nsApiNodes", environment],
@@ -32,22 +35,24 @@ export default function AccountPageButtonGroup({ address }: Props) {
if (nymNode.bonding_address)
return (
<ExplorerButtonGroup
onPage="Account"
options={[
{
label: "Nym Node",
isSelected: false,
link: nymNode
? `/nym-node/${nymNode.node_id}`
: `/account/${address}/not-found`,
},
{
label: "Account",
isSelected: true,
link: `/account/${address}`,
},
]}
/>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<ExplorerButtonGroup
onPage="Account"
options={[
{
label: "Nym Node",
isSelected: false,
link: nymNode
? `${basePath}/nym-node/${nymNode.node_id}`
: `${basePath}/account/${address}/not-found`,
},
{
label: "Account",
isSelected: true,
link: `${basePath}/account/${address}`,
},
]}
/>
</Box>
);
}
@@ -28,7 +28,7 @@ export const DesktopHeader = () => {
}}
>
<Link
href={"/"}
href={"https://nym.com/"}
style={{
display: "flex",
alignItems: "center",
@@ -3,11 +3,14 @@ import React from "react";
import { Typography, Button, Link as MuiLink } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { useEnvironment } from "../../providers/EnvironmentProvider";
import { useRouter, usePathname } from "next/navigation";
import { getBasePathByEnv } from "../../../envs/config";
export const Environment: React.FC = () => {
const theme = useTheme();
const { environment, setEnvironment } = useEnvironment();
const router = useRouter();
const pathname = usePathname();
const explorerName = environment
? `${environment} Explorer`
@@ -16,10 +19,23 @@ export const Environment: React.FC = () => {
const switchNetworkText =
environment === "mainnet" ? "Switch to Testnet" : "Switch to Mainnet";
const switchNetworkLink = environment === "mainnet" ? "/" : "/";
const getCurrentInternalPath = () => {
// Remove the base path from the current pathname to get the internal path
return pathname.replace(/^\/(explorer|sandbox-explorer)/, "") || "/";
};
const handleSwitchEnvironment = () => {
setEnvironment(environment === "mainnet" ? "sandbox" : "mainnet");
const newEnvironment = environment === "mainnet" ? "sandbox" : "mainnet";
setEnvironment(newEnvironment);
// Get the current internal path and build the new path
const currentInternalPath = getCurrentInternalPath();
const newBasePath = getBasePathByEnv(newEnvironment);
const newPath =
currentInternalPath === "/"
? newBasePath
: `${newBasePath}${currentInternalPath}`;
router.push(newPath);
};
return (
@@ -33,7 +49,7 @@ export const Environment: React.FC = () => {
}}
>
<MuiLink
href="/"
href={getBasePathByEnv(environment || "mainnet")}
underline="none"
color="inherit"
textTransform="capitalize"
@@ -45,7 +61,6 @@ export const Environment: React.FC = () => {
variant="outlined"
color="inherit"
onClick={handleSwitchEnvironment}
href={switchNetworkLink}
sx={{
borderRadius: 2,
textTransform: "none",
@@ -4,6 +4,8 @@ import { Circle } from "@mui/icons-material";
import { Button, Stack } from "@mui/material";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEnvironment } from "../../providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../envs/config";
import type { MenuItem } from "./menuItems";
type HeaderItemProps = {
@@ -12,10 +14,13 @@ type HeaderItemProps = {
const HeaderItem = ({ menu }: HeaderItemProps) => {
const pathname = usePathname();
const { environment } = useEnvironment();
const basePath = getBasePathByEnv(environment || "mainnet");
return (
<Stack direction="row" gap={2} key={menu.id} alignItems="center">
{pathname.includes(menu.url) && <Circle sx={{ fontSize: 10 }} />}
<Link href={menu.url} passHref>
<Link href={`${basePath}${menu.url}`} passHref>
<Button
sx={{
padding: 0,
@@ -145,7 +145,7 @@ const MobileMenuHeader = ({
>
<Link
onClick={() => toggleDrawer(false)}
href={"/"}
href={"https://nym.com/"}
style={{
display: "flex",
alignItems: "center",
@@ -31,7 +31,7 @@ export const TokenomicsCard = () => {
isLoading: isEpochLoading,
isError: isEpochError,
} = useQuery({
queryKey: ["epochRewards"],
queryKey: ["epochRewards", environment],
queryFn: () => fetchEpochRewards(environment),
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
@@ -44,7 +44,7 @@ export const TokenomicsCard = () => {
isLoading: isStakingLoading,
isError: isStakingError,
} = useQuery({
queryKey: ["noise"],
queryKey: ["noise", environment],
queryFn: () => fetchNoise(environment),
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
@@ -32,6 +32,7 @@ import ConnectWallet from "../wallet/ConnectWallet";
import type { MappedNymNode, MappedNymNodes } from "./NodeTableWithAction";
import CopyToClipboard from "../copyToClipboard/CopyToClipboard";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../envs/config";
const ColumnHeading = ({
children,
@@ -510,9 +511,11 @@ const NodeTable = ({ nodes }: { nodes: MappedNymNodes }) => {
muiTableBodyRowProps: ({ row }) => ({
onClick: (e) => {
if (e.ctrlKey || e.metaKey) {
window.open(`/explorer/nym-node/${row.original.nodeId}`, "_blank");
const basePath = getBasePathByEnv(environment || "mainnet");
window.open(`${basePath}/nym-node/${row.original.nodeId}`, "_blank");
} else {
router.push(`/nym-node/${row.original.nodeId}`);
const basePath = getBasePathByEnv(environment || "mainnet");
router.push(`${basePath}/nym-node/${row.original.nodeId}`);
}
},
hover: true,
@@ -160,7 +160,7 @@ const NodeTableWithAction = () => {
isLoading: isEpochLoading,
isError: isEpochError,
} = useQuery({
queryKey: ["epochRewards"],
queryKey: ["epochRewards", environment],
queryFn: () => fetchEpochRewards(environment),
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
@@ -12,6 +12,8 @@ import { useRouter } from "next/navigation";
import { useMemo } from "react";
import type { NodeRewardDetails } from "../../app/api/types";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../envs/config";
const ColumnHeading = ({
children,
}: {
@@ -37,6 +39,7 @@ const DelegationsTable = ({ id }: Props) => {
const router = useRouter();
const theme = useTheme();
const { environment } = useEnvironment();
const basePath = getBasePathByEnv(environment || "mainnet");
const { data: delegations = [], isError } = useQuery({
queryKey: ["nodeDelegations", id, environment],
@@ -140,7 +143,7 @@ const DelegationsTable = ({ id }: Props) => {
},
muiTableBodyRowProps: ({ row }) => ({
onClick: () => {
router.push(`/account/${row.original.owner}`);
router.push(`${basePath}/account/${row.original.owner}`);
},
hover: true,
sx: {
@@ -5,6 +5,9 @@ import type { NS_NODE } from "@/app/api/types";
import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { useQuery } from "@tanstack/react-query";
import { getBasePathByEnv } from "../../../envs/config";
import { Box } from "@mui/material";
import SectionHeading from "@/components/headings/SectionHeading";
type Props = {
paramId: string;
@@ -13,6 +16,7 @@ type Props = {
export default function NodePageButtonGroup({ paramId }: Props) {
let nodeInfo: NS_NODE | undefined;
const { environment } = useEnvironment();
const basePath = getBasePathByEnv(environment || "mainnet");
const { data: nsApiNodes = [], isError: isNSApiNodesError } = useQuery({
queryKey: ["nsApiNodes", environment],
@@ -41,20 +45,22 @@ export default function NodePageButtonGroup({ paramId }: Props) {
if (nodeInfo.bonding_address)
return (
<ExplorerButtonGroup
onPage="Nym Node"
options={[
{
label: "Nym Node",
isSelected: true,
link: `/nym-node/${nodeInfo.node_id}`,
},
{
label: "Account",
isSelected: false,
link: `/account/${nodeInfo.bonding_address}`,
},
]}
/>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<ExplorerButtonGroup
onPage="Node"
options={[
{
label: "Nym Node",
isSelected: true,
link: `${basePath}/nym-node/${nodeInfo.identity_key}`,
},
{
label: "Account",
isSelected: false,
link: `${basePath}/account/${nodeInfo.bonding_address}`,
},
]}
/>
</Box>
);
}
@@ -15,6 +15,8 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { fetchNSApiNodes } from "../../app/api";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../envs/config";
const NodeAndAddressSearch = () => {
const router = useRouter();
const { environment } = useEnvironment();
@@ -24,7 +26,6 @@ const NodeAndAddressSearch = () => {
const [searchOptions, setSearchOptions] = useState<NS_NODE[]>([]);
// Use React Query to fetch nodes
const { data: nsApiNodes = [], isLoading: isNSApiNodesLoading } = useQuery({
queryKey: ["nsApiNodes", environment],
queryFn: () => fetchNSApiNodes(environment),
@@ -35,8 +36,13 @@ const NodeAndAddressSearch = () => {
});
const handleSearch = async () => {
setErrorText(""); // Clear any previous error messages
setIsLoading(true); // Start loading
if (!inputValue.trim()) {
setErrorText("Please enter a search term");
return;
}
setIsLoading(true);
setErrorText("");
try {
if (inputValue.startsWith("n1")) {
@@ -47,7 +53,8 @@ const NodeAndAddressSearch = () => {
try {
const data = await response.json();
if (data) {
router.push(`/account/${inputValue}`);
const basePath = getBasePathByEnv(environment || "mainnet");
router.push(`${basePath}/account/${inputValue}`);
return;
}
} catch {
@@ -74,7 +81,8 @@ const NodeAndAddressSearch = () => {
);
if (matchingNode) {
router.push(`/nym-node/${matchingNode.identity_key}`);
const basePath = getBasePathByEnv(environment || "mainnet");
router.push(`${basePath}/nym-node/${matchingNode.identity_key}`);
return;
}
}
@@ -122,7 +130,8 @@ const NodeAndAddressSearch = () => {
) => {
if (value && typeof value !== "string") {
setIsLoading(true); // Show loading spinner
router.push(`/nym-node/${value.node_id}`);
const basePath = getBasePathByEnv(environment || "mainnet");
router.push(`${basePath}/nym-node/${value.node_id}`);
}
};
@@ -20,7 +20,7 @@ const OriginalStakeCard = () => {
isLoading,
isError,
} = useQuery({
queryKey: ["originalStake", address],
queryKey: ["originalStake", address, environment],
queryFn: () => fetchOriginalStake(address || "", environment),
enabled: !!address, // Only fetch if address exists
staleTime: 10 * 60 * 1000, // 10 minutes
@@ -38,6 +38,7 @@ import StakeModal from "./StakeModal";
import type { MappedNymNode, MappedNymNodes } from "./StakeTableWithAction";
import { fee } from "./schemas";
import { useEnvironment } from "@/providers/EnvironmentProvider";
import { getBasePathByEnv } from "../../../envs/config";
type DelegationWithNodeDetails = {
node: MappedNymNode | undefined;
@@ -98,6 +99,7 @@ const StakeTable = ({ nodes }: { nodes: MappedNymNodes }) => {
const chain = environment === "mainnet" ? COSMOS_KIT_USE_CHAIN : "sandbox";
const { isWalletConnected } = useChain(chain);
const { data: pendingEvents } = usePendingEvents(nymQueryClient, address);
const basePath = getBasePathByEnv(environment || "mainnet");
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
@@ -654,7 +656,9 @@ const StakeTable = ({ nodes }: { nodes: MappedNymNodes }) => {
},
muiTableBodyRowProps: ({ row }) => ({
onClick: () => {
router.push(`/nym-node/${row.original.node?.nodeId || "not-found"}`);
router.push(
`${basePath}/nym-node/${row.original.node?.nodeId || "not-found"}`
);
},
hover: true,
sx: {
@@ -74,7 +74,7 @@ const StakeTableWithAction = () => {
isLoading: isEpochLoading,
isError: isEpochError,
} = useQuery({
queryKey: ["epochRewards"],
queryKey: ["epochRewards", environment],
queryFn: () => fetchEpochRewards(environment),
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
@@ -49,31 +49,31 @@ const SubHeaderRowActions = () => {
isLoading: isDelegationsLoading,
isError: isDelegationsError,
} = useQuery({
queryKey: ["delegations", address],
queryKey: ["delegations", address, environment],
queryFn: () => fetchDelegations(address || "", nymClient),
enabled: !!address && !!nymClient, // Only fetch if address and nymClient are available
});
// Fetch total rewards using React Query
const {
data: totalStakerRewards = 0,
isLoading: isRewardsLoading,
isError: isRewardsError,
refetch,
} = useQuery({
queryKey: ["totalStakerRewards", address],
queryFn: () => fetchTotalStakerRewards(address || "", environment),
enabled: !!address, // Only fetch if address is available
enabled: !!address && !!nymClient, // Only fetch if address and nymClient exist
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
refetchOnReconnect: false,
});
// Fetch total rewards using React Query
const {
data: totalStakerRewards = 0,
isLoading: isTotalStakerRewardsLoading,
isError: isTotalStakerRewardsError,
} = useQuery({
queryKey: ["totalStakerRewards", address, environment],
queryFn: () => fetchTotalStakerRewards(address || "", environment),
enabled: !!address, // Only fetch if address exists
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
refetchOnReconnect: false,
});
const handleRefetch = useCallback(async () => {
await refetch();
await queryClient.invalidateQueries(); // This will refetch ALL active queries
}, [queryClient, refetch]);
}, [queryClient]);
const handleRedeemRewards = useCallback(async () => {
setIsLoading(true);
@@ -155,11 +155,11 @@ const SubHeaderRowActions = () => {
return null;
}
if (isDelegationsLoading || isRewardsLoading) {
if (isDelegationsLoading || isTotalStakerRewardsLoading) {
return <Loading />;
}
if (isDelegationsError || isRewardsError) {
if (isTotalStakerRewardsError) {
return (
<Stack direction="row" spacing={3} justifyContent={"end"}>
<Button variant="contained" disabled>
@@ -169,6 +169,7 @@ const SubHeaderRowActions = () => {
);
}
return (
<Stack direction="row" spacing={3} justifyContent={"end"}>
<Button
@@ -20,7 +20,7 @@ const TotalRewardsCard = () => {
isLoading,
isError,
} = useQuery({
queryKey: ["totalStakerRewards", address],
queryKey: ["totalStakerRewards", address, environment],
queryFn: () => fetchTotalStakerRewards(address || "", environment),
enabled: !!address, // Only fetch if address exists
staleTime: 10 * 60 * 1000, // 10 minutes
@@ -20,7 +20,7 @@ const TotalStakeCard = () => {
isLoading,
isError,
} = useQuery({
queryKey: ["totalStake", address],
queryKey: ["totalStake", address, environment],
queryFn: () => fetchBalances(address || "", environment),
enabled: !!address, // Only fetch if address exists
staleTime: 10 * 60 * 1000, // 10 minutes
@@ -53,6 +53,7 @@ const TotalStakeCard = () => {
);
}
return (
<ExplorerCard label="Total Stake">
<Typography
@@ -1,10 +1,8 @@
import type { PendingEpochEventKind } from "@nymproject/contract-clients/Mixnet.types";
import { useQuery } from "@tanstack/react-query";
import { useEnvironment } from "../providers/EnvironmentProvider";
export const getEventsByAddress = (
kind: PendingEpochEventKind,
address: string,
) => {
export const getEventsByAddress = (kind: PendingEpochEventKind, address: string) => {
if ("delegate" in kind && kind.delegate.owner === address) {
return {
kind: "delegate" as const,
@@ -27,8 +25,10 @@ export type PendingEvent = ReturnType<typeof getEventsByAddress>;
// Custom Hook for fetching pending events
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const usePendingEvents = (nymQueryClient: any, address: string | undefined) => {
const { environment } = useEnvironment();
return useQuery({
queryKey: ["pendingEvents", address], // Query key to uniquely identify this query
queryKey: ["pendingEvents", address, environment], // Query key to uniquely identify this query
queryFn: async () => {
if (!nymQueryClient || !address) {
throw new Error("Missing required dependencies");
@@ -47,6 +47,9 @@ const usePendingEvents = (nymQueryClient: any, address: string | undefined) => {
return pendingEvents;
},
enabled: !!nymQueryClient && !!address, // Prevents execution if dependencies are missing
staleTime: 10 * 60 * 1000, // 10 minutes
refetchOnWindowFocus: false, // Prevents unnecessary refetching
refetchOnReconnect: false,
});
};
@@ -29,7 +29,7 @@ const useGetWalletBalance = () => {
isError,
refetch,
} = useQuery({
queryKey: ["nymBalance", address],
queryKey: ["nymBalance", address, environment],
queryFn: () => fetchNYMBalance(address || "", getCosmWasmClient),
enabled: !!address, // Only fetch if address exists
});
+1
View File
@@ -57,6 +57,7 @@ export const useNymClient = () => {
getCosmWasmClient,
getSigningCosmWasmClient,
mixnetContractAddress,
environment,
]);
return { nymClient, nymQueryClient, address };
@@ -1,8 +1,8 @@
"use client";
import React, { createContext, useContext } from "react";
import dynamic from "next/dynamic";
import React, { createContext, useContext, useState, useEffect } from "react";
import { usePathname } from "next/navigation";
type Environment = "mainnet" | "sandbox";
export type Environment = "mainnet" | "sandbox";
interface EnvironmentContextType {
environment: Environment;
@@ -13,42 +13,29 @@ const EnvironmentContext = createContext<EnvironmentContextType | undefined>(
undefined
);
const ENVIRONMENT_STORAGE_KEY = "environment";
const ClientStorage = dynamic(
() =>
import("@uidotdev/usehooks").then((mod) => {
const { useLocalStorage } = mod;
return function ClientStorageComponent({
children,
}: {
children: React.ReactNode;
}) {
const [stored, setStored] = useLocalStorage<{ env: Environment }>(
ENVIRONMENT_STORAGE_KEY,
{ env: "mainnet" }
);
const setEnvironment = (env: Environment) => {
setStored({ env });
};
return (
<EnvironmentContext.Provider
value={{ environment: stored.env, setEnvironment }}
>
{children}
</EnvironmentContext.Provider>
);
};
}),
{ ssr: false }
);
export const EnvironmentProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return <ClientStorage>{children}</ClientStorage>;
const [environment, setEnvironment] = useState<Environment>("mainnet");
const pathname = usePathname();
// Initialize environment from URL path
useEffect(() => {
if (pathname.startsWith("/sandbox-explorer")) {
setEnvironment("sandbox");
} else if (pathname.startsWith("/explorer")) {
setEnvironment("mainnet");
} else {
// Default to mainnet for other paths
setEnvironment("mainnet");
}
}, [pathname]);
return (
<EnvironmentContext.Provider value={{ environment, setEnvironment }}>
{children}
</EnvironmentContext.Provider>
);
};
export const useEnvironment = () => {