fix payment input

This commit is contained in:
PhilReact 2024-12-21 01:57:33 +02:00
parent cdcdfa66fb
commit e9dddfbf88
5 changed files with 295 additions and 47 deletions

View File

@ -126,6 +126,7 @@ import { handleGetFileFromIndexedDB } from "./utils/indexedDB";
import { Wallets } from "./Wallets";
import { useHandleTutorials } from "./components/Tutorials/useHandleTutorials";
import { Tutorials } from "./components/Tutorials/Tutorials";
import BoundedNumericTextField from "./common/BoundedNumericTextField";
type extStates =
@ -745,41 +746,52 @@ function App() {
setLtcBalanceLoading(false);
});
};
const sendCoinFunc = () => {
setSendPaymentError("");
setSendPaymentSuccess("");
if (!paymentTo) {
setSendPaymentError("Please enter a recipient");
return;
}
if (!paymentAmount) {
setSendPaymentError("Please enter an amount greater than 0");
return;
}
if (!paymentPassword) {
setSendPaymentError("Please enter your wallet password");
return;
}
setIsLoading(true);
window
.sendMessage("sendCoin", {
amount: Number(paymentAmount),
receiver: paymentTo.trim(),
password: paymentPassword,
const sendCoinFunc = async() => {
try {
setSendPaymentError("");
setSendPaymentSuccess("");
if (!paymentTo) {
setSendPaymentError("Please enter a recipient");
return;
}
if (!paymentAmount) {
setSendPaymentError("Please enter an amount greater than 0");
return;
}
if (!paymentPassword) {
setSendPaymentError("Please enter your wallet password");
return;
}
const fee = await getFee('PAYMENT')
await show({
message: `Would you like to transfer ${Number(paymentAmount)} QORT?` ,
paymentFee: fee.fee + ' QORT'
})
.then((response) => {
if (response?.error) {
setSendPaymentError(response.error);
} else {
setIsOpenSendQort(false);
setIsOpenSendQortSuccess(true);
}
setIsLoading(false);
})
.catch((error) => {
console.error("Failed to send coin:", error);
setIsLoading(false);
});
setIsLoading(true);
window
.sendMessage("sendCoin", {
amount: Number(paymentAmount),
receiver: paymentTo.trim(),
password: paymentPassword,
})
.then((response) => {
if (response?.error) {
setSendPaymentError(response.error);
} else {
setIsOpenSendQort(false);
setIsOpenSendQortSuccess(true);
}
setIsLoading(false);
})
.catch((error) => {
console.error("Failed to send coin:", error);
setIsLoading(false);
});
} catch (error) {
//error
}
};
const clearAllStates = () => {
@ -1854,12 +1866,14 @@ console.log('openTutorialModal3', openTutorialModal)
Amount
</CustomLabel>
<Spacer height="5px" />
<CustomInput
id="standard-adornment-amount"
type="number"
<BoundedNumericTextField
value={paymentAmount}
onChange={(e) => setPaymentAmount(+e.target.value)}
autoComplete="off"
minValue={0}
maxValue={+balance}
allowDecimals={true}
initialValue={'0'}
allowNegatives={false}
afterChange={(e: string) => setPaymentAmount(+e)}
/>
<Spacer height="6px" />
<CustomLabel htmlFor="standard-adornment-password">
@ -2895,14 +2909,21 @@ await showInfo({
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Publish"}</DialogTitle>
<DialogTitle id="alert-dialog-title">{message.paymentFee ? "Payment" : "Publish"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{message.message}
</DialogContentText>
<DialogContentText id="alert-dialog-description2">
publish fee: {message.publishFee}
</DialogContentText>
{message?.paymentFee && (
<DialogContentText id="alert-dialog-description2">
payment fee: {message.paymentFee}
</DialogContentText>
)}
{message?.publishFee && (
<DialogContentText id="alert-dialog-description2">
publish fee: {message.publishFee}
</DialogContentText>
)}
</DialogContent>
<DialogActions>
<Button sx={{

View File

@ -2154,7 +2154,7 @@ export async function sendCoin(
const lastRef = await getLastRef();
const fee = await sendQortFee();
const validApi = await findUsableApi();
const validApi = null;
const res = await makeTransactionRequest(
confirmReceiver,

View File

@ -0,0 +1,159 @@
import {
IconButton,
InputAdornment,
TextField,
TextFieldProps,
} from "@mui/material";
import React, { useRef, useState } from "react";
import AddIcon from "@mui/icons-material/Add";
import RemoveIcon from "@mui/icons-material/Remove";
import {
removeTrailingZeros,
setNumberWithinBounds,
} from "./numberFunctions.ts";
import { CustomInput } from "../App-styles.ts";
type eventType = React.ChangeEvent<HTMLInputElement>;
type BoundedNumericTextFieldProps = {
minValue: number;
maxValue: number;
addIconButtons?: boolean;
allowDecimals?: boolean;
allowNegatives?: boolean;
afterChange?: (s: string) => void;
initialValue?: string;
maxSigDigits?: number;
} & TextFieldProps;
export const BoundedNumericTextField = ({
minValue,
maxValue,
addIconButtons = true,
allowDecimals = true,
allowNegatives = false,
afterChange,
initialValue,
maxSigDigits = 6,
...props
}: BoundedNumericTextFieldProps) => {
const [textFieldValue, setTextFieldValue] = useState<string>(
initialValue || ""
);
const ref = useRef<HTMLInputElement | null>(null);
const stringIsEmpty = (value: string) => {
return value === "";
};
const isAllZerosNum = /^0*\.?0*$/;
const isFloatNum = /^-?[0-9]*\.?[0-9]*$/;
const isIntegerNum = /^-?[0-9]+$/;
const skipMinMaxCheck = (value: string) => {
const lastIndexIsDecimal = value.charAt(value.length - 1) === ".";
const isEmpty = stringIsEmpty(value);
const isAllZeros = isAllZerosNum.test(value);
const isInteger = isIntegerNum.test(value);
// skipping minMax on all 0s allows values less than 1 to be entered
return lastIndexIsDecimal || isEmpty || (isAllZeros && !isInteger);
};
const setMinMaxValue = (value: string): string => {
if (skipMinMaxCheck(value)) return value;
const valueNum = Number(value);
const boundedNum = setNumberWithinBounds(valueNum, minValue, maxValue);
const numberInBounds = boundedNum === valueNum;
return numberInBounds ? value : boundedNum.toString();
};
const getSigDigits = (number: string) => {
if (isIntegerNum.test(number)) return 0;
const decimalSplit = number.split(".");
return decimalSplit[decimalSplit.length - 1].length;
};
const sigDigitsExceeded = (number: string, sigDigits: number) => {
return getSigDigits(number) > sigDigits;
};
const filterTypes = (value: string) => {
if (allowDecimals === false) value = value.replace(".", "");
if (allowNegatives === false) value = value.replace("-", "");
if (sigDigitsExceeded(value, maxSigDigits)) {
value = value.substring(0, value.length - 1);
}
return value;
};
const filterValue = (value: string) => {
if (stringIsEmpty(value)) return "";
value = filterTypes(value);
if (isFloatNum.test(value)) {
return setMinMaxValue(value);
}
return textFieldValue;
};
const listeners = (e: eventType) => {
// console.log("changeEvent:", e);
const newValue = filterValue(e.target.value);
setTextFieldValue(newValue);
if (afterChange) afterChange(newValue);
};
const changeValueWithIncDecButton = (changeAmount: number) => {
const changedValue = (+textFieldValue + changeAmount).toString();
const inBoundsValue = setMinMaxValue(changedValue);
setTextFieldValue(inBoundsValue);
if (afterChange) afterChange(inBoundsValue);
};
const formatValueOnBlur = (e: eventType) => {
let value = e.target.value;
if (stringIsEmpty(value) || value === ".") {
setTextFieldValue("");
return;
}
value = setMinMaxValue(value);
value = removeTrailingZeros(value);
if (isAllZerosNum.test(value)) value = minValue.toString();
setTextFieldValue(value);
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { onChange, ...noChangeProps } = { ...props };
return (
<CustomInput
{...noChangeProps}
InputProps={{
...props?.InputProps,
endAdornment: addIconButtons ? (
<InputAdornment position="end">
<IconButton size="small" onClick={() => changeValueWithIncDecButton(1)}>
<AddIcon sx={{
color: 'white'
}} />{" "}
</IconButton>
<IconButton size="small" onClick={() => changeValueWithIncDecButton(-1)}>
<RemoveIcon sx={{
color: 'white'
}} />{" "}
</IconButton>
</InputAdornment>
) : (
<></>
),
}}
onChange={e => listeners(e as eventType)}
onBlur={e => {
formatValueOnBlur(e as eventType);
}}
autoComplete="off"
value={textFieldValue}
inputRef={ref}
/>
);
};
export default BoundedNumericTextField;

View File

@ -0,0 +1,63 @@
export const truncateNumber = (value: string | number, sigDigits: number) => {
return Number(value).toFixed(sigDigits);
};
export const removeTrailingZeros = (s: string) => {
return Number(s).toString();
};
export const setNumberWithinBounds = (
num: number,
minValue: number,
maxValue: number
) => {
if (num > maxValue) return maxValue;
if (num < minValue) return minValue;
return num;
};
export const numberToInt = (num: number) => {
return Math.floor(num);
};
type ByteFormat = "Decimal" | "Binary";
export function formatBytes(
bytes: number,
decimals = 2,
format: ByteFormat = "Binary"
) {
if (bytes === 0) return "0 Bytes";
const k = format === "Binary" ? 1024 : 1000;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
export function formatTime(seconds: number): string {
seconds = Math.floor(seconds);
const minutes: number | string = Math.floor(seconds / 60);
let hours: number | string = Math.floor(minutes / 60);
let remainingSeconds: number | string = seconds % 60;
let remainingMinutes: number | string = minutes % 60;
if (remainingSeconds < 10) {
remainingSeconds = "0" + remainingSeconds;
}
if (remainingMinutes < 10) {
remainingMinutes = "0" + remainingMinutes;
}
if (hours === 0) {
hours = "";
} else {
hours = hours + ":";
}
return hours + remainingMinutes + ":" + remainingSeconds;
}

View File

@ -1,6 +1,7 @@
import { Button, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material";
import { Button, ButtonBase, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material";
import { useState } from 'react'
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
export const CustomInput = styled(TextField)({
width: "183px", // Adjust the width as needed
borderRadius: "5px",
@ -51,7 +52,11 @@ export const PasswordField: React.FunctionComponent<TextFieldProps> = ({ ...prop
<InputAdornment position="end" data-testid="toggle-view-password-btn" onClick={() => {
setCanViewPassword((prevState) => !prevState)
}}>
{canViewPassword ? <Button data-testid="plain-text-indicator" sx={{ minWidth: 0, p: 0 }}>👁</Button> : <Button data-testid="password-text-indicator" sx={{ minWidth: 0, p: 0 }}>👁🗨</Button>}
{canViewPassword ? <ButtonBase data-testid="plain-text-indicator" sx={{ minWidth: 0, p: 0 }}><VisibilityOffIcon sx={{
color: 'white'
}}/></ButtonBase> : <ButtonBase data-testid="password-text-indicator" sx={{ minWidth: 0, p: 0 }}><VisibilityIcon sx={{
color: 'white'
}} /></ButtonBase>}
</InputAdornment>
)
}}