Files
nym/explorer-v2/src/components/staking/StakeModal.tsx
T
2025-04-10 11:14:54 +03:00

147 lines
4.1 KiB
TypeScript

import { Button, Stack, Typography } from "@mui/material";
import { CurrencyFormField } from "@nymproject/react/currency/CurrencyFormField.js";
import { IdentityKeyFormField } from "@nymproject/react/mixnodes/IdentityKeyFormField.js";
import type { DecCoin } from "@nymproject/types";
import { useCallback, useEffect, useState } from "react";
import SimpleModal from "../../components/modal/SimpleModal";
import useGetWalletBalance from "../../hooks/useGetWalletBalance";
import ExplorerListItem from "../list/ListItem";
import stakingSchema, { MIN_AMOUNT_TO_DELEGATE } from "./schemas";
const StakeModal = ({
nodeId,
identityKey,
onStake,
onClose,
}: {
nodeId?: number;
identityKey?: string;
onStake: ({
nodeId,
amount,
}: {
nodeId: number;
amount: string;
}) => Promise<void>;
onClose: () => void;
}) => {
const { balance } = useGetWalletBalance();
const [amount, setAmount] = useState<DecCoin>({
amount: MIN_AMOUNT_TO_DELEGATE,
denom: "nym",
});
const [isValidated, setValidated] = useState<boolean>(false);
const [errorAmount, setErrorAmount] = useState<string | undefined>();
const validateAmount = useCallback(async () => {
try {
await stakingSchema.parseAsync({
amount: amount.amount,
balance,
nodeId,
});
setValidated(true);
setErrorAmount(undefined);
} catch (e) {
if (e instanceof Error && "errors" in e) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const validationError = (e as any).errors; // Explicitly cast if necessary
console.error(validationError);
setValidated(false);
setErrorAmount(validationError[0]?.message);
} else {
console.error("Unknown error during validation:", e);
setValidated(false);
setErrorAmount("An unexpected error occurred.");
}
}
}, [amount, balance, nodeId]);
useEffect(() => {
if (nodeId) {
validateAmount();
}
}, [validateAmount, nodeId]);
if (!nodeId) {
return null;
}
const handleOnClose = () => {
setAmount({ amount: MIN_AMOUNT_TO_DELEGATE, denom: "nym" });
onClose();
};
return (
<SimpleModal
title="Stake"
open={!!identityKey}
onClose={handleOnClose}
Actions={
<Button
variant="contained"
color="secondary"
onClick={() => onStake({ nodeId, amount: amount.amount })}
fullWidth
disabled={!isValidated}
>
Next
</Button>
}
>
<Stack spacing={3}>
<Stack spacing={0.5}>
<Typography variant="body5">Address</Typography>
<IdentityKeyFormField
placeholder="Identity Key"
required
fullWidth
initialValue={identityKey}
readOnly
showTickOnValid={false}
sx={{
"& .MuiInputBase-input": {
color: "rgba(0, 0, 0, 0.87)",
"&::placeholder": {
color: "rgba(0, 0, 0, 0.54)",
},
},
"& .Mui-disabled": {
color: "rgba(0, 0, 0, 0.38)",
},
}}
/>
</Stack>
<Stack spacing={0.5}>
<Typography variant="body5">Amount</Typography>
<CurrencyFormField
placeholder="Amount"
showCoinMark={false}
required
fullWidth
autoFocus
initialValue={amount.amount}
onChanged={setAmount}
denom={"nym"}
validationError={errorAmount}
sx={{
"& .MuiInputBase-input": {
color: "rgba(0, 0, 0, 0.87)",
"&::placeholder": {
color: "rgba(0, 0, 0, 0.54)",
},
},
"& .Mui-disabled": {
color: "rgba(0, 0, 0, 0.38)",
},
}}
/>
</Stack>
<ExplorerListItem row label="Account balance" value={balance} divider />
</Stack>
</SimpleModal>
);
};
export default StakeModal;