2022-01-11 03:41:42 +00:00
|
|
|
import clsx from 'clsx'
|
2022-05-26 21:41:24 +00:00
|
|
|
import React from 'react'
|
2022-05-09 13:04:36 +00:00
|
|
|
import { useUser } from 'web/hooks/use-user'
|
2022-05-17 03:27:37 +00:00
|
|
|
import { formatMoney } from 'common/util/format'
|
2022-01-11 03:41:42 +00:00
|
|
|
import { Col } from './layout/col'
|
2022-05-03 20:36:54 +00:00
|
|
|
import { SiteLink } from './site-link'
|
2022-05-17 12:17:22 +00:00
|
|
|
import { ENV_CONFIG } from 'common/envs/constants'
|
2022-09-27 00:28:54 +00:00
|
|
|
import { useWindowSize } from 'web/hooks/use-window-size'
|
|
|
|
import { Row } from './layout/row'
|
2022-01-11 03:41:42 +00:00
|
|
|
|
|
|
|
export function AmountInput(props: {
|
|
|
|
amount: number | undefined
|
|
|
|
onChange: (newAmount: number | undefined) => void
|
|
|
|
error: string | undefined
|
2022-03-29 19:56:56 +00:00
|
|
|
label: string
|
2022-01-11 03:41:42 +00:00
|
|
|
disabled?: boolean
|
|
|
|
className?: string
|
|
|
|
inputClassName?: string
|
2022-01-26 20:08:03 +00:00
|
|
|
// Needed to focus the amount input
|
|
|
|
inputRef?: React.MutableRefObject<any>
|
2022-01-11 03:41:42 +00:00
|
|
|
}) {
|
|
|
|
const {
|
|
|
|
amount,
|
|
|
|
onChange,
|
|
|
|
error,
|
2022-03-29 19:56:56 +00:00
|
|
|
label,
|
2022-01-11 03:41:42 +00:00
|
|
|
disabled,
|
|
|
|
className,
|
|
|
|
inputClassName,
|
2022-01-26 20:08:03 +00:00
|
|
|
inputRef,
|
2022-01-11 03:41:42 +00:00
|
|
|
} = props
|
|
|
|
|
|
|
|
const onAmountChange = (str: string) => {
|
2022-04-07 03:48:06 +00:00
|
|
|
const amount = parseInt(str.replace(/\D/g, ''))
|
|
|
|
const isInvalid = !str || isNaN(amount)
|
|
|
|
onChange(isInvalid ? undefined : amount)
|
2022-01-11 03:41:42 +00:00
|
|
|
}
|
2022-09-27 00:28:54 +00:00
|
|
|
|
|
|
|
const { width } = useWindowSize()
|
|
|
|
const isMobile = (width ?? 0) < 768
|
|
|
|
|
2022-01-11 03:41:42 +00:00
|
|
|
return (
|
2022-09-27 00:28:54 +00:00
|
|
|
<>
|
|
|
|
<Col className={className}>
|
|
|
|
<label className="font-sm md:font-lg">
|
|
|
|
<span className={clsx('text-greyscale-4 absolute ml-2 mt-[9px]')}>
|
|
|
|
{label}
|
|
|
|
</span>
|
|
|
|
<input
|
|
|
|
className={clsx(
|
|
|
|
'placeholder:text-greyscale-4 border-greyscale-2 rounded-md pl-9',
|
|
|
|
error && 'input-error',
|
|
|
|
isMobile ? 'w-24' : '',
|
|
|
|
inputClassName
|
|
|
|
)}
|
|
|
|
ref={inputRef}
|
|
|
|
type="text"
|
|
|
|
pattern="[0-9]*"
|
|
|
|
inputMode="numeric"
|
|
|
|
placeholder="0"
|
|
|
|
maxLength={6}
|
|
|
|
autoFocus={!isMobile}
|
|
|
|
value={amount ?? ''}
|
|
|
|
disabled={disabled}
|
|
|
|
onChange={(e) => onAmountChange(e.target.value)}
|
|
|
|
/>
|
|
|
|
</label>
|
2022-03-02 03:31:48 +00:00
|
|
|
|
2022-09-27 00:28:54 +00:00
|
|
|
{error && (
|
|
|
|
<div className="absolute mt-11 whitespace-nowrap text-xs font-medium tracking-wide text-red-500">
|
|
|
|
{error === 'Insufficient balance' ? (
|
|
|
|
<>
|
|
|
|
Not enough funds.
|
|
|
|
<span className="ml-1 text-indigo-500">
|
|
|
|
<SiteLink href="/add-funds">Buy more?</SiteLink>
|
|
|
|
</span>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
error
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Col>
|
|
|
|
</>
|
2022-03-29 19:56:56 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function BuyAmountInput(props: {
|
|
|
|
amount: number | undefined
|
|
|
|
onChange: (newAmount: number | undefined) => void
|
|
|
|
error: string | undefined
|
|
|
|
setError: (error: string | undefined) => void
|
|
|
|
minimumAmount?: number
|
|
|
|
disabled?: boolean
|
2022-09-05 22:11:32 +00:00
|
|
|
showSliderOnMobile?: boolean
|
2022-03-29 19:56:56 +00:00
|
|
|
className?: string
|
|
|
|
inputClassName?: string
|
|
|
|
// Needed to focus the amount input
|
|
|
|
inputRef?: React.MutableRefObject<any>
|
|
|
|
}) {
|
|
|
|
const {
|
|
|
|
amount,
|
|
|
|
onChange,
|
|
|
|
error,
|
|
|
|
setError,
|
2022-09-05 22:11:32 +00:00
|
|
|
showSliderOnMobile: showSlider,
|
2022-03-29 19:56:56 +00:00
|
|
|
disabled,
|
|
|
|
className,
|
|
|
|
inputClassName,
|
|
|
|
minimumAmount,
|
|
|
|
inputRef,
|
|
|
|
} = props
|
|
|
|
|
|
|
|
const user = useUser()
|
|
|
|
|
|
|
|
const onAmountChange = (amount: number | undefined) => {
|
|
|
|
onChange(amount)
|
|
|
|
|
|
|
|
// Check for errors.
|
|
|
|
if (amount !== undefined) {
|
2022-04-13 17:52:12 +00:00
|
|
|
if (user && user.balance < amount) {
|
2022-03-29 19:56:56 +00:00
|
|
|
setError('Insufficient balance')
|
|
|
|
} else if (minimumAmount && amount < minimumAmount) {
|
|
|
|
setError('Minimum amount: ' + formatMoney(minimumAmount))
|
|
|
|
} else {
|
|
|
|
setError(undefined)
|
|
|
|
}
|
2022-08-19 17:07:48 +00:00
|
|
|
} else {
|
|
|
|
setError(undefined)
|
2022-03-29 19:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-09 04:55:33 +00:00
|
|
|
const parseRaw = (x: number) => {
|
|
|
|
if (x <= 100) return x
|
|
|
|
if (x <= 130) return 100 + (x - 100) * 5
|
|
|
|
return 250 + (x - 130) * 10
|
|
|
|
}
|
|
|
|
|
|
|
|
const getRaw = (x: number) => {
|
|
|
|
if (x <= 100) return x
|
|
|
|
if (x <= 250) return 100 + (x - 100) / 5
|
|
|
|
return 130 + (x - 250) / 10
|
|
|
|
}
|
|
|
|
|
2022-03-29 19:56:56 +00:00
|
|
|
return (
|
2022-09-05 22:11:32 +00:00
|
|
|
<>
|
2022-09-27 00:28:54 +00:00
|
|
|
<Row className="gap-4">
|
|
|
|
<AmountInput
|
|
|
|
amount={amount}
|
|
|
|
onChange={onAmountChange}
|
|
|
|
label={ENV_CONFIG.moneyMoniker}
|
|
|
|
error={error}
|
|
|
|
disabled={disabled}
|
|
|
|
className={className}
|
|
|
|
inputClassName={inputClassName}
|
|
|
|
inputRef={inputRef}
|
2022-09-05 22:11:32 +00:00
|
|
|
/>
|
2022-09-27 00:28:54 +00:00
|
|
|
{showSlider && (
|
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="0"
|
|
|
|
max="205"
|
|
|
|
value={getRaw(amount ?? 0)}
|
|
|
|
onChange={(e) => onAmountChange(parseRaw(parseInt(e.target.value)))}
|
2022-09-29 14:04:11 +00:00
|
|
|
className="range range-lg only-thumb my-auto align-middle xl:hidden"
|
2022-09-27 00:28:54 +00:00
|
|
|
step="5"
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</Row>
|
2022-09-05 22:11:32 +00:00
|
|
|
</>
|
2022-03-29 19:56:56 +00:00
|
|
|
)
|
|
|
|
}
|