Skip to content

Commit

Permalink
Merge branch 'main' into estimated-duration
Browse files Browse the repository at this point in the history
  • Loading branch information
kpyszkowski authored Aug 21, 2024
2 parents 561eaf9 + 183e193 commit e5cd707
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 43 deletions.
2 changes: 1 addition & 1 deletion dapp/src/components/TransactionModal/ActionFormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function ActionFormModal({ type }: { type: ActionFlowType }) {
<>
{!isLoading && <ModalCloseButton />}
<ModalHeader>{heading}</ModalHeader>
<ModalBody>
<ModalBody px={10}>
<Box w="100%">
<FormComponent onSubmitForm={handleSubmitFormWrapper} />
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import TokenAmountForm from "#/components/shared/TokenAmountForm"
import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase"
import { useMinDepositAmount, useWallet } from "#/hooks"
import { FormSubmitButton } from "#/components/shared/Form"
import { BaseFormProps } from "#/types"
import { ACTION_FLOW_TYPES, BaseFormProps } from "#/types"
import { fixedPointNumberToString, getCurrencyByType } from "#/utils"
import StakeDetails from "./StakeDetails"

function StakeFormModal({
Expand All @@ -12,11 +13,16 @@ function StakeFormModal({
const minDepositAmount = useMinDepositAmount()
const { balance: tokenBalance } = useWallet()

const { decimals } = getCurrencyByType("bitcoin")
const inputPlaceholder = `Minimum ${fixedPointNumberToString(minDepositAmount, decimals)} BTC`
const tokenAmountLabel = "Wallet balance"

return (
<TokenAmountForm
tokenBalanceInputPlaceholder="BTC"
actionType={ACTION_FLOW_TYPES.STAKE}
tokenBalanceInputPlaceholder={inputPlaceholder}
tokenAmountLabel={tokenAmountLabel}
currency="bitcoin"
fiatCurrency="usd"
tokenBalance={tokenBalance ?? 0n}
minTokenAmount={minDepositAmount}
onSubmitForm={onSubmitForm}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import React from "react"
import TokenAmountForm from "#/components/shared/TokenAmountForm"
import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase"
import { FormSubmitButton } from "#/components/shared/Form"
import { BaseFormProps, PROCESS_STATUSES } from "#/types"
import { ACTION_FLOW_TYPES, BaseFormProps, PROCESS_STATUSES } from "#/types"
import {
useActionFlowStatus,
useBitcoinPosition,
useMinWithdrawAmount,
} from "#/hooks"
import { fixedPointNumberToString, getCurrencyByType } from "#/utils"
import UnstakeDetails from "./UnstakeDetails"

function UnstakeFormModal({
Expand All @@ -18,11 +19,16 @@ function UnstakeFormModal({
const minTokenAmount = useMinWithdrawAmount()
const status = useActionFlowStatus()

const { decimals } = getCurrencyByType("bitcoin")
const inputPlaceholder = `Minimum ${fixedPointNumberToString(minTokenAmount, decimals)} BTC`
const tokenAmountLabel = "Acre balance"

return (
<TokenAmountForm
tokenBalanceInputPlaceholder="BTC"
actionType={ACTION_FLOW_TYPES.UNSTAKE}
tokenBalanceInputPlaceholder={inputPlaceholder}
tokenAmountLabel={tokenAmountLabel}
currency="bitcoin"
fiatCurrency="usd"
tokenBalance={balance}
minTokenAmount={minTokenAmount}
onSubmitForm={onSubmitForm}
Expand Down
3 changes: 2 additions & 1 deletion dapp/src/components/shared/Form/FormSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { ButtonProps } from "@chakra-ui/react"
import { LoadingButton } from "../LoadingButton"

export function FormSubmitButton({ children, ...props }: ButtonProps) {
const { isSubmitting } = useFormikContext()
const { isSubmitting, isValid } = useFormikContext()

return (
<LoadingButton
type="submit"
size="lg"
width="100%"
isLoading={isSubmitting}
isDisabled={!isValid}
{...props}
>
{children}
Expand Down
7 changes: 5 additions & 2 deletions dapp/src/components/shared/Form/FormTokenBalanceInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type FormTokenBalanceInputProps = {
name: string
defaultValue?: bigint
} & Omit<TokenBalanceInputProps, "setAmount" | "defaultValue">

export function FormTokenBalanceInput({
name,
defaultValue,
Expand All @@ -17,9 +18,11 @@ export function FormTokenBalanceInput({
const setAmount = useCallback(
(value?: bigint) => {
if (!meta.touched) logPromiseFailure(helpers.setTouched(true))
if (meta.error) helpers.setError(undefined)

logPromiseFailure(helpers.setValue(value))
},
[helpers, meta.touched],
[helpers, meta.touched, meta.error],
)

useEffect(() => {
Expand All @@ -34,7 +37,7 @@ export function FormTokenBalanceInput({
{...field}
amount={defaultValue ?? (meta.value as bigint)}
setAmount={setAmount}
hasError={Boolean(meta.touched && meta.error)}
hasError={Boolean(meta.error)}
errorMsgText={meta.error}
/>
)
Expand Down
31 changes: 25 additions & 6 deletions dapp/src/components/shared/NumberFormatInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from "react"
import { NumericFormat, NumericFormatProps } from "react-number-format"
import {
NumberFormatValues,
NumericFormat,
NumericFormatProps,
} from "react-number-format"
import { InputProps, chakra, useMultiStyleConfig } from "@chakra-ui/react"

const ChakraWrapper = chakra(NumericFormat)
const ChakraNumericFormat = chakra(NumericFormat)

export type NumberFormatInputValues = {
formattedValue: string
Expand All @@ -12,8 +16,9 @@ export type NumberFormatInputValues = {

export type NumberFormatInputProps = {
onValueChange: (values: NumberFormatInputValues) => void
integerScale?: number
} & InputProps &
Pick<NumericFormatProps, "decimalScale" | "allowNegative">
Pick<NumericFormatProps, "decimalScale" | "allowNegative" | "suffix">

/**
* Component is from the Threshold Network React Components repository.
Expand All @@ -29,17 +34,31 @@ const NumberFormatInput = React.forwardRef<
>((props, ref) => {
const { field: css } = useMultiStyleConfig("Input", props)

const { decimalScale, isDisabled, isInvalid, ...restProps } = props
const { decimalScale, isDisabled, isInvalid, integerScale, ...restProps } =
props

const handleLengthValidation = (values: NumberFormatValues) => {
const { value, floatValue } = values
if (
floatValue === undefined ||
value === undefined ||
integerScale === undefined
)
return true

const [integerPart] = value.split(".")
return integerPart.length <= integerScale
}

return (
<ChakraWrapper
allowLeadingZeros={false}
<ChakraNumericFormat
thousandSeparator
decimalScale={decimalScale}
__css={css}
disabled={isDisabled}
aria-invalid={isInvalid}
getInputRef={ref}
isAllowed={handleLengthValidation}
{...restProps}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export type TokenAmountFormBaseProps = {
formId?: string
tokenBalance: bigint
tokenBalanceInputPlaceholder: string
tokenAmountLabel?: string
currency: CurrencyType
withMaxButton: boolean
fiatCurrency?: CurrencyType
children?: React.ReactNode
defaultAmount?: bigint
}
Expand All @@ -35,8 +35,8 @@ export default function TokenAmountFormBase({
formId,
tokenBalance,
currency,
fiatCurrency,
tokenBalanceInputPlaceholder,
tokenAmountLabel,
withMaxButton,
children,
defaultAmount,
Expand All @@ -48,10 +48,11 @@ export default function TokenAmountFormBase({
name={TOKEN_AMOUNT_FIELD_NAME}
tokenBalance={tokenBalance}
placeholder={tokenBalanceInputPlaceholder}
tokenAmountLabel={tokenAmountLabel}
currency={currency}
fiatCurrency={fiatCurrency}
withMaxButton={withMaxButton}
defaultValue={defaultAmount}
autoFocus
/>
{children}
</Form>
Expand Down
11 changes: 9 additions & 2 deletions dapp/src/components/shared/TokenAmountForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { FormikErrors, withFormik } from "formik"
import { getErrorsObj, validateTokenAmount } from "#/utils"
import { BaseFormProps } from "#/types"
import { ActionFlowType, BaseFormProps } from "#/types"
import TokenAmountFormBase, {
TokenAmountFormBaseProps,
TokenAmountFormValues,
} from "./TokenAmountFormBase"

type TokenAmountFormProps = {
actionType: ActionFlowType
minTokenAmount: bigint
} & TokenAmountFormBaseProps &
BaseFormProps<TokenAmountFormValues>
Expand All @@ -16,10 +17,14 @@ const TokenAmountForm = withFormik<TokenAmountFormProps, TokenAmountFormValues>(
mapPropsToValues: () => ({
amount: undefined,
}),
validate: ({ amount }, { tokenBalance, currency, minTokenAmount }) => {
validate: (
{ amount },
{ tokenBalance, currency, minTokenAmount, actionType },
) => {
const errors: FormikErrors<TokenAmountFormValues> = {}

errors.amount = validateTokenAmount(
actionType,
amount,
tokenBalance,
minTokenAmount,
Expand All @@ -31,6 +36,8 @@ const TokenAmountForm = withFormik<TokenAmountFormProps, TokenAmountFormValues>(
handleSubmit: (values, { props }) => {
props.onSubmitForm(values)
},
validateOnBlur: false,
validateOnChange: false,
},
)(TokenAmountFormBase)

Expand Down
34 changes: 26 additions & 8 deletions dapp/src/components/shared/TokenBalanceInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef } from "react"
import React, { ChangeEventHandler, useRef } from "react"
import {
Box,
Button,
Expand All @@ -15,6 +15,7 @@ import {
import {
fixedPointNumberToString,
getCurrencyByType,
isFormError,
userAmountToBigInt,
} from "#/utils"
import { CurrencyType } from "#/types"
Expand All @@ -25,6 +26,7 @@ import NumberFormatInput, {
NumberFormatInputProps,
} from "../NumberFormatInput"
import { CurrencyBalance } from "../CurrencyBalance"
import { Alert, AlertIcon, AlertDescription } from "../Alert"

const VARIANT = "balance"

Expand All @@ -42,7 +44,12 @@ function HelperErrorText({
if (hasError) {
return (
<FormErrorMessage>
{errorMsgText || "Please enter a valid value"}
<Alert status="error">
<AlertIcon status="error" />
<AlertDescription>
{errorMsgText || "Please enter a valid value"}
</AlertDescription>
</Alert>
</FormErrorMessage>
)
}
Expand Down Expand Up @@ -102,6 +109,7 @@ export type TokenBalanceInputProps = {
fiatCurrency?: CurrencyType
setAmount: (value?: bigint) => void
withMaxButton?: boolean
tokenAmountLabel?: string
} & InputProps &
HelperErrorTextProps &
Pick<NumberFormatInputProps, "decimalScale">
Expand All @@ -118,27 +126,37 @@ export default function TokenBalanceInput({
hasError = false,
fiatCurrency,
withMaxButton = false,
tokenAmountLabel = "Amount",
...inputProps
}: TokenBalanceInputProps) {
const valueRef = useRef<bigint | undefined>(amount)
const styles = useMultiStyleConfig("TokenBalanceInput", { size })

const { decimals } = getCurrencyByType(currency)
const { decimals, symbol } = getCurrencyByType(currency)

const handleValueChange = (value: string) => {
valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined
}

const handleChange: ChangeEventHandler = () => {
setAmount(valueRef?.current)
}

const isBalanceExceeded =
typeof errorMsgText === "string" &&
isFormError("EXCEEDED_VALUE", errorMsgText)

return (
<FormControl isInvalid={hasError} isDisabled={inputProps.isDisabled}>
<FormLabel htmlFor={inputProps.name} size={size}>
<FormLabel htmlFor={inputProps.name} size={size} mr={0}>
<Box __css={styles.labelContainer}>
Amount
<Box __css={styles.balanceContainer}>
<Box as="span" __css={styles.balance}>
Balance
{tokenAmountLabel}
</Box>
<CurrencyBalance
color={isBalanceExceeded ? "red.400" : "gray.700"}
size={size === "lg" ? "md" : "sm"}
amount={tokenBalance}
currency={currency}
Expand All @@ -152,18 +170,18 @@ export default function TokenBalanceInput({
variant={VARIANT}
isInvalid={hasError}
placeholder={placeholder}
suffix={` ${symbol}`}
{...inputProps}
value={
amount ? fixedPointNumberToString(amount, decimals) : undefined
}
onValueChange={(values: NumberFormatInputValues) =>
handleValueChange(values.value)
}
onChange={() => {
setAmount(valueRef?.current)
}}
onChange={handleChange}
decimalScale={decimals}
allowNegative={false}
integerScale={10}
/>

{withMaxButton && (
Expand Down
18 changes: 17 additions & 1 deletion dapp/src/constants/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ConnectionErrorData } from "#/types"
import { ACTION_FLOW_TYPES, ConnectionErrorData } from "#/types"

export const CONNECTION_ERRORS: Record<string, ConnectionErrorData> = {
REJECTED: {
Expand All @@ -24,3 +24,19 @@ export const CONNECTION_ERRORS: Record<string, ConnectionErrorData> = {
description: "We encountered an error. Please try again.",
},
}

export const TOKEN_FORM_ERRORS = {
REQUIRED: "Please enter an amount.",
EXCEEDED_VALUE:
"The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.",
INSUFFICIENT_VALUE: (minValue: string) =>
`The amount is below the minimum required deposit of ${minValue} BTC.`,
}

export const ACTION_FORM_ERRORS = {
[ACTION_FLOW_TYPES.STAKE]: TOKEN_FORM_ERRORS,
[ACTION_FLOW_TYPES.UNSTAKE]: {
...TOKEN_FORM_ERRORS,
EXCEEDED_VALUE: "Your Acre balance is insufficient.",
},
}
Loading

0 comments on commit e5cd707

Please sign in to comment.