Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ const protectedRoutes = [
];

function AppContent() {
const { isConnected, isConnecting, isDisconnected } = useAccount();
const location = useLocation();
const navigate = useNavigate();
const { isConnected, isConnecting } = useAccount();
const [hasConnected, setHasConnected] = useState<boolean>(false);
const [isInitialized, setIsInitialized] = useState<boolean>(false);
const [redirectRoute, setRedirectRoute] = useState<string>("");

Expand All @@ -39,6 +40,15 @@ function AppContent() {
return () => clearTimeout(timer);
}, []);

useEffect(() => {
if (isConnected && !hasConnected) {
setHasConnected(true);
} else if (hasConnected && isDisconnected) {
setHasConnected(false);
window.sessionStorage.removeItem("offline-address");
}
}, [isConnected, hasConnected, isDisconnected]);

useEffect(() => {
if (isConnected && protectedRoutes.includes(redirectRoute)) {
setRedirectRoute("");
Expand All @@ -65,9 +75,6 @@ function AppContent() {
return null;
}

const isDashboardNoWallet =
location.pathname === "/dashboard" && !isConnected;

return (
<Box id="root" className="flex h-dvh overflow-hidden">
<Sidebar />
Expand All @@ -80,7 +87,8 @@ function AppContent() {
<Routes />
</Box>
</Box>
{!isDashboardNoWallet && <Footer />}

<Footer />
</Box>
</ScrollToTop>
</Box>
Expand Down
110 changes: 110 additions & 0 deletions src/components/ConnectWallet/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { AccountBalanceWallet as WalletIcon } from "@mui/icons-material";
import { Box, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import { isAddress } from "viem";
import { useChainId, useConnect } from "wagmi";

import { OfflineConnector } from "@/config/OfflineConnector";
import { OfflineConnectModal } from "@/modals/OfflineConnect";

export const ConnectWallet = () => {
const { connect } = useConnect();
const chainId = useChainId();

const [showOfflineConnect, setShowOfflineConnect] = useState(false);

useEffect(() => {
const previousAddress = window.sessionStorage.getItem("offline-address");

if (!previousAddress) {
return;
}

if (isAddress(previousAddress)) {
onOfflineConnectModalClose(previousAddress as `0x${string}`);
} else {
window.sessionStorage.removeItem("offline-address");
}
}, []);

const onOfflineConnectModalClose = (address: `0x${string}` | undefined) => {
if (address) {
connect({
connector: OfflineConnector({
address,
defaultChainId: chainId,
}),
});

window.sessionStorage.setItem("offline-address", address);
}

setShowOfflineConnect(false);
};

return (
<Box className="flex min-h-full w-full items-center justify-center">
<Box
className="max-w-md px-6 text-center"
sx={{
marginTop: "-80px",
animation: "fadeIn 0.6s ease-in",
"@keyframes fadeIn": {
from: { opacity: 0, transform: "translateY(20px)" },
to: { opacity: 1, transform: "translateY(0)" },
},
}}
>
<Box
className="mb-6 flex justify-center"
sx={{
animation: "float 3s ease-in-out infinite",
"@keyframes float": {
"0%, 100%": { transform: "translateY(0px)" },
"50%": { transform: "translateY(-10px)" },
},
}}
>
<WalletIcon
sx={{
fontSize: 80,
color: "#627EEA",
filter: "drop-shadow(0 0 20px rgba(98, 126, 234, 0.5))",
}}
/>
</Box>
<Typography
variant="h4"
className="mb-4 font-bold text-white"
sx={{ fontSize: { xs: "1.75rem", md: "2.125rem" } }}
>
Connect Your Wallet
</Typography>
<Typography
variant="body1"
className="mb-8 text-gray-300"
sx={{ fontSize: { xs: "0.95rem", md: "1.05rem" } }}
>
Connect your wallet to access your validator dashboard, manage your
stake, and monitor your Ethereum validators.
</Typography>
<Box className="flex justify-center mb-2">
<appkit-button namespace="eip155" />
</Box>
<Box className="mt-4">
<Typography
className="cursor-pointer text-sm"
onClick={() => setShowOfflineConnect(true)}
>
or specify an address for offline wallets
</Typography>
</Box>
</Box>

<OfflineConnectModal
open={showOfflineConnect}
onClose={onOfflineConnectModalClose}
/>
</Box>
);
};
1 change: 1 addition & 0 deletions src/components/ConnectWallet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ConnectWallet";
42 changes: 42 additions & 0 deletions src/components/CopyToClipboard/CopyToCliboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Box } from "@mui/material";
import { ReactNode, useCallback, useState } from "react";

interface CopyToClipboardProps {
children: ReactNode;
text: string;
}

export const CopyToClipboard = ({ children, text }: CopyToClipboardProps) => {
const [copySuccessful, setCopySuccessful] = useState<boolean>(false);
const isSupported = !!navigator?.clipboard;

const handleCopy = useCallback(async () => {
if (!isSupported) {
return;
}

try {
await navigator.clipboard.writeText(text);
setCopySuccessful(true);
const timer = setTimeout(() => setCopySuccessful(false), 1500);
return () => clearTimeout(timer);
} catch (e) {
console.error("Failed to copy: ", e);
}
}, [text, isSupported]);

return (
<Box
className={isSupported && "cursor-pointer relative"}
onClick={handleCopy}
>
{children}

{copySuccessful && (
<Box className="absolute inset-0 bg-black/50 flex items-center justify-center text-white text-lg font-semibold">
Copied Successfully
</Box>
)}
</Box>
);
};
1 change: 1 addition & 0 deletions src/components/CopyToClipboard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CopyToCliboard";
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
CustomTableRow,
} from "@/components/CustomTable";
import { ExplorerLink } from "@/components/ExplorerLink";
import { FilterInput } from "@/components/FilterInput";
import { FilterInput } from "@/components/Input";
import { PendingValidatorBalanceChange } from "@/components/PendingValidatorBalanceChange";
import { ValidatorMenu } from "@/components/ValidatorMenu";
import { ValidatorState } from "@/components/ValidatorState";
Expand Down
4 changes: 2 additions & 2 deletions src/components/ExitValidatorTable/ExitValidatorTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {
TableBody,
TableSortLabel,
} from "@mui/material";
import { useEffect, useMemo, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";

import {
CustomTableCell,
CustomTableHeaderCell,
CustomTableRow,
} from "@/components/CustomTable";
import { ExplorerLink } from "@/components/ExplorerLink";
import { FilterInput } from "@/components/FilterInput";
import { FilterInput } from "@/components/Input";
import { ValidatorsWrapper } from "@/components/ValidatorsWrapper";
import { useSelectedValidator } from "@/context/SelectedValidatorContext";
import { useValidators } from "@/hooks/useValidators";
Expand Down
43 changes: 43 additions & 0 deletions src/components/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TextField } from "@mui/material";
import { Dispatch, SetStateAction } from "react";

interface InputParams<T> {
placeholder: string;
value: T;
setValue: Dispatch<SetStateAction<T>>;
}

export const Input = <T,>({ placeholder, value, setValue }: InputParams<T>) => {
return (
<TextField
fullWidth
size="small"
placeholder={placeholder}
value={value}
onChange={(e) => setValue(e.target.value as T)}
sx={{
"& .MuiOutlinedInput-root": {
color: "#ffffff",
backgroundColor: "#333743",
"& fieldset": {
borderColor: "#404040",
},
"&:hover fieldset": {
borderColor: "#606060",
},
"&.Mui-focused fieldset": {
borderColor: "#627EEA",
},
},
"& .MuiInputBase-input": {
color: "#ffffff",
padding: "8px 12px",
},
"& .MuiInputBase-input::placeholder": {
color: "#b3b3b3",
opacity: 1,
},
}}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./FilterInput";
export * from "./Input";
Loading