Add basepath on environment change
This commit is contained in:
@@ -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;
|
||||
// };
|
||||
@@ -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 does’t 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
|
||||
});
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
Reference in New Issue
Block a user