diff --git a/src/App.tsx b/src/App.tsx index 6e58e83..eb5b5b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -133,6 +133,8 @@ import { RandomSentenceGenerator } from "./utils/seedPhrase/RandomSentenceGenera import { useFetchResources } from "./common/useFetchResources"; import { Tutorials } from "./components/Tutorials/Tutorials"; import { useHandleTutorials } from "./components/Tutorials/useHandleTutorials"; +import { removeLeadingZero } from "./utils/helpers"; +import BoundedNumericTextField from "./common/BoundedNumericTextField"; type extStates = | "not-authenticated" @@ -657,7 +659,8 @@ function App() { setLtcBalanceLoading(false); }); }; - const sendCoinFunc = () => { + const sendCoinFunc = async() => { + try { setSendPaymentError(""); setSendPaymentSuccess(""); if (!paymentTo) { @@ -672,6 +675,12 @@ function App() { 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' + }) setIsLoading(true); window .sendMessage("sendCoin", { @@ -692,6 +701,9 @@ function App() { console.error("Failed to send coin:", error); setIsLoading(false); }); + } catch (error) { + // error + } }; const clearAllStates = () => { @@ -1778,12 +1790,22 @@ function App() { Amount - setPaymentAmount(+e.target.value)} autoComplete="off" + onInput={removeLeadingZero} + /> */} + setPaymentAmount(+e)} /> @@ -2822,14 +2844,21 @@ function App() { aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - {"Publish"} + {message.paymentFee ? "Payment" : "Publish"} {message.message} - - publish fee: {message.publishFee} - + {message?.paymentFee && ( + + payment fee: {message.paymentFee} + + )} + {message?.publishFee && ( + + publish fee: {message.publishFee} + + )} diff --git a/src/background.ts b/src/background.ts index bef13d3..f466154 100644 --- a/src/background.ts +++ b/src/background.ts @@ -2282,7 +2282,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, diff --git a/src/common/BoundedNumericTextField.tsx b/src/common/BoundedNumericTextField.tsx new file mode 100644 index 0000000..c997f8a --- /dev/null +++ b/src/common/BoundedNumericTextField.tsx @@ -0,0 +1,158 @@ +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; +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( + initialValue || "" + ); + const ref = useRef(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) => { + 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 ( + + changeValueWithIncDecButton(1)}> + {" "} + + changeValueWithIncDecButton(-1)}> + {" "} + + + ) : ( + <> + ), + }} + onChange={e => listeners(e as eventType)} + onBlur={e => { + formatValueOnBlur(e as eventType); + }} + autoComplete="off" + value={textFieldValue} + inputRef={ref} + /> + ); +}; + +export default BoundedNumericTextField; diff --git a/src/common/numberFunctions.ts b/src/common/numberFunctions.ts new file mode 100644 index 0000000..583196c --- /dev/null +++ b/src/common/numberFunctions.ts @@ -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; +}