diff --git a/nym-wallet/src/components/Bonding/modals/NodeCostParametersModals.tsx b/nym-wallet/src/components/Bonding/modals/NodeCostParametersModals.tsx index a3b7aadf27..80c052ea3d 100644 --- a/nym-wallet/src/components/Bonding/modals/NodeCostParametersModals.tsx +++ b/nym-wallet/src/components/Bonding/modals/NodeCostParametersModals.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useEffect, useState } from 'react'; -import { Typography } from '@mui/material'; +import { Typography, Box, Alert } from '@mui/material'; import { TBondedNode } from 'src/context'; import { useGetFee } from 'src/hooks/useGetFee'; import { ModalFee } from '../../Modals/ModalFee'; @@ -32,7 +32,9 @@ export const UpdateCostParametersModal = ({ }: Props) => { const { fee, isFeeLoading, getFee, feeError } = useGetFee(); const [hasFetchedFee, setHasFetchedFee] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + // Handle fee errors useEffect(() => { if (feeError) { onError(feeError); @@ -50,7 +52,7 @@ export const UpdateCostParametersModal = ({ try { const decimalProfitMargin = (parseFloat(profitMarginPercent) / 100).toString(); - const uNymAmount = String(Math.floor(Number(intervalOperatingCost) * 1000000)); + const uNymAmount = String(Math.floor(Number(intervalOperatingCost || '0') * 1000000)); const costParams: NodeCostParams = { profit_margin_percent: decimalProfitMargin, @@ -61,7 +63,6 @@ export const UpdateCostParametersModal = ({ }; getFee(simulateUpdateMixnodeCostParams, costParams); - setHasFetchedFee(true); } catch (error) { onError(error as string); @@ -69,18 +70,33 @@ export const UpdateCostParametersModal = ({ } }, [hasFetchedFee, intervalOperatingCost, profitMarginPercent, getFee, onError, node]); + // Handle confirmation with loading state + const handleConfirm = async () => { + if (isSubmitting) return; + + try { + setIsSubmitting(true); + await onConfirm(); + } catch (error) { + onError(error as string); + } finally { + setIsSubmitting(false); + } + }; + return ( + These changes will affect your node's economics and delegator rewards. Your new profit margin and operating cost will be applied in the next interval. + + {/* Warning message */} + + + + This action will overwrite your existing profit margin and operating cost settings. + Only one cost parameter update is allowed per epoch. + + + ); }; \ No newline at end of file diff --git a/nym-wallet/src/components/Send/SendInputModal.tsx b/nym-wallet/src/components/Send/SendInputModal.tsx index 0ac77f4b28..dc21ece24d 100644 --- a/nym-wallet/src/components/Send/SendInputModal.tsx +++ b/nym-wallet/src/components/Send/SendInputModal.tsx @@ -121,6 +121,19 @@ export const SendInputModal = ({ initialValue={amount?.amount} denom={denom} /> + onMemoChange(e.target.value)} + value={memo} + error={!memoIsValid} + placeholder="Optional" + helperText={ + !memoIsValid ? 'The text is invalid, only alphanumeric characters and white spaces are allowed' : undefined + } + InputLabelProps={{ shrink: true }} + fullWidth + /> {error} @@ -144,22 +157,8 @@ export const SendInputModal = ({ initialValue={userFees?.amount} fullWidth /> - onMemoChange(e.target.value)} - value={memo} - error={!memoIsValid} - helperText={ - !memoIsValid - ? ' The text is invalid, only alphanumeric characters and white spaces are allowed' - : undefined - } - InputLabelProps={{ shrink: true }} - fullWidth - /> )} ); -}; +}; \ No newline at end of file diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/NodeCostParameters.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/NodeCostParameters.tsx index 3a2a0211f3..e8c8d4c717 100644 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/NodeCostParameters.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/settings-pages/NodeCostParameters.tsx @@ -11,9 +11,15 @@ interface Props { bondedNode: TBondedNode; onConfirm: () => Promise; onError: (e: string) => void; + onUpdateData?: (profitMarginPercent: string, intervalOperatingCost: string, fee?: FeeDetails) => void; } -export const NodeCostParametersPage = ({ bondedNode, onConfirm, onError }: Props) => { +export const NodeCostParametersPage = ({ + bondedNode, + onConfirm, + onError, + onUpdateData +}: Props) => { const { updateCostParameters } = useBondingContext(); const [intervalOperatingCost, setIntervalOperatingCost] = useState(''); const [profitMarginPercent, setProfitMarginPercent] = useState(''); @@ -33,6 +39,12 @@ export const NodeCostParametersPage = ({ bondedNode, onConfirm, onError }: Props } }, [bondedNode]); + useEffect(() => { + if (onUpdateData && isFormValid) { + onUpdateData(profitMarginPercent, intervalOperatingCost, fee); + } + }, [profitMarginPercent, intervalOperatingCost, fee, isFormValid, onUpdateData]); + const handleIntervalOperatingCostChange = (e: React.ChangeEvent) => { const value = e.target.value; if (value === '' || /^[0-9]*\.?[0-9]*$/.test(value)) { @@ -63,6 +75,10 @@ export const NodeCostParametersPage = ({ bondedNode, onConfirm, onError }: Props try { const uNymAmount = String(Math.floor(Number(intervalOperatingCost) * 1000000)); + if (onUpdateData) { + onUpdateData(profitMarginPercent, intervalOperatingCost, fee); + } + await updateCostParameters(profitMarginPercent, uNymAmount, fee); setIsConfirmed(false); onConfirm(); @@ -120,7 +136,8 @@ export const NodeCostParametersPage = ({ bondedNode, onConfirm, onError }: Props InputProps={{ endAdornment: %, }} - helperText="Input your profit margin (e.g., 20 for 20%)" + helperText="Input your profit margin (must be between 20% and 50%)" + error={profitMarginPercent !== '' && (Number(profitMarginPercent) < 20 || Number(profitMarginPercent) > 50)} />