Skip to content

Commit

Permalink
Add a basic input for token balance
Browse files Browse the repository at this point in the history
  • Loading branch information
kkosiorowska committed Dec 5, 2023
1 parent 9f5fe24 commit 9564273
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 14 deletions.
24 changes: 14 additions & 10 deletions dapp/src/components/Modals/StakeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import {
Tab,
TabPanels,
TabPanel,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
import { useStakingFlowContext } from "../../hooks"
import { useStakingFlowContext, useWalletContext } from "../../hooks"
import BaseModal from "./BaseModal"
import { TokenBalance } from "../TokenBalance"
import { BITCOIN } from "../../constants"
import TokenBalanceInput from "../TokenBalanceInput"

function StakeDetails({
text,
Expand All @@ -41,7 +39,8 @@ function StakeDetails({
}

export default function ActionModal() {
const { closeModal } = useStakingFlowContext()
const { amount, setAmount, closeModal } = useStakingFlowContext()
const { btcAccount } = useWalletContext()

return (
<BaseModal>
Expand All @@ -55,11 +54,16 @@ export default function ActionModal() {
<TabPanels>
<TabPanel>
<Flex gap={12} direction="column">
{/* TODO: Create a custom number input component */}
<NumberInput>
<NumberInputField />
<NumberInputStepper />
</NumberInput>
{/* TODO: Add a validation */}
<TokenBalanceInput
amount={amount}
// TODO: Use the real data
usdAmount="45.725,91"
tokenBalance={btcAccount?.balance.toString() ?? 0}
currency={BITCOIN}
placeholder={BITCOIN.symbol}
onChange={(value) => setAmount(value)}
/>
<Flex gap={4} direction="column" w="100%">
{/* TODO: Use the real data */}
<StakeDetails
Expand Down
78 changes: 78 additions & 0 deletions dapp/src/components/TokenBalanceInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useMemo } from "react"
import {
Button,
Flex,
HStack,
Icon,
InputGroup,
InputRightElement,
NumberInput,
NumberInputField,
Text,
Tooltip,
useColorModeValue,
} from "@chakra-ui/react"
import { TokenBalance } from "../TokenBalance"
import { Currency } from "../../types"
import { fixedPointNumberToString } from "../../utils"
import { Alert } from "../../static/icons"
import { USD } from "../../constants"

export default function TokenBalanceInput({
amount,
usdAmount,
currency,
tokenBalance,
placeholder,
onChange,
}: {
amount?: string
usdAmount?: string
currency: Currency
tokenBalance: string | number
placeholder?: string
onChange: (value: string) => void
}) {
// TODO: Set the correct color
const colorInfo = useColorModeValue("grey.200", "grey.200")

const tokenBalanceAmount = useMemo(
() =>
fixedPointNumberToString(BigInt(tokenBalance || 0), currency.decimals),
[currency.decimals, tokenBalance],
)

return (
<Flex direction="column" gap={2}>
<Flex justifyContent="space-between">
<Text>Amount</Text>
<HStack>
<Text>Balance</Text>
<TokenBalance tokenBalance={tokenBalance} currency={currency} />
</HStack>
</Flex>
<InputGroup>
<NumberInput
w="100%"
min={0}
value={amount}
onChange={(valueString) => onChange(valueString)}
>
<NumberInputField placeholder={placeholder} />
</NumberInput>
<InputRightElement width="5rem">
<Button h="1.75rem" onClick={() => onChange(tokenBalanceAmount)}>
Max
</Button>
</InputRightElement>
</InputGroup>
<HStack>
{/* TODO: Add correct text for tooltip */}
<Tooltip label="Template">
<Icon as={Alert} color={colorInfo} />
</Tooltip>
<Text color={colorInfo}>{`${usdAmount} ${USD.symbol}`}</Text>
</HStack>
</Flex>
)
}
14 changes: 10 additions & 4 deletions dapp/src/contexts/StakingFlowContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import React, { createContext, useCallback, useMemo, useState } from "react"
import { ModalType } from "../types"

type StakingFlowContextValue = {
modalType: ModalType | undefined
modalType?: ModalType
amount?: string
closeModal: () => void
setModalType: React.Dispatch<React.SetStateAction<ModalType | undefined>>
setAmount: React.Dispatch<React.SetStateAction<string | undefined>>
}

export const StakingFlowContext = createContext<StakingFlowContextValue>({
modalType: undefined,
setModalType: () => {},
closeModal: () => {},
setModalType: () => {},
setAmount: () => {},
})

export function StakingFlowProvider({
Expand All @@ -19,19 +21,23 @@ export function StakingFlowProvider({
children: React.ReactNode
}): React.ReactElement {
const [modalType, setModalType] = useState<ModalType | undefined>(undefined)
const [amount, setAmount] = useState<string | undefined>(undefined)

const closeModal = useCallback(() => {
setModalType(undefined)
setAmount(undefined)
}, [])

const contextValue: StakingFlowContextValue =
useMemo<StakingFlowContextValue>(
() => ({
modalType,
amount,
closeModal,
setModalType,
setAmount,
}),
[modalType, closeModal],
[modalType, amount, closeModal],
)

return (
Expand Down
31 changes: 31 additions & 0 deletions dapp/src/static/icons/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react"
import { createIcon } from "@chakra-ui/react"

export const Alert = createIcon({
displayName: "Alert",
viewBox: "0 0 16 16",
path: (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_3178_2454)">
<path
d="M7.99967 5.33325V7.99992M7.99967 10.6666H8.00634M14.6663 7.99992C14.6663 11.6818 11.6816 14.6666 7.99967 14.6666C4.31778 14.6666 1.33301 11.6818 1.33301 7.99992C1.33301 4.31802 4.31778 1.33325 7.99967 1.33325C11.6816 1.33325 14.6663 4.31802 14.6663 7.99992Z"
stroke="#675E60"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0_3178_2454">
<rect width="16" height="16" fill="currentColor" />
</clipPath>
</defs>
</svg>
),
})
1 change: 1 addition & 0 deletions dapp/src/static/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./Info"
export * from "./Bitcoin"
export * from "./Ethereum"
export * from "./ChevronRight"
export * from "./Alert"
39 changes: 39 additions & 0 deletions dapp/src/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,42 @@ export const formatSatoshiAmount = (
amount: number | string,
desiredDecimals = 2,
) => formatTokenAmount(amount, 8, desiredDecimals)

/**
* Converts a fixed point number with a bigint amount and a decimals field
* indicating the orders of magnitude in `amount` behind the decimal point into
* a string in US decimal format (no thousands separators, . for the decimal
* separator).
*
* Used in cases where precision is critical.
*
* This function is based on the solution used by the Taho extension.
* More info: https://github.com/tahowallet/extension/blob/main/background/lib/fixed-point.ts#L172-L214
*/
export function fixedPointNumberToString(
amount: bigint,
decimals: number,
trimTrailingZeros = true,
): string {
const undecimaledAmount = amount.toString()
const preDecimalLength = undecimaledAmount.length - decimals

const preDecimalCharacters =
preDecimalLength > 0
? undecimaledAmount.substring(0, preDecimalLength)
: "0"
const postDecimalCharacters =
"0".repeat(Math.max(-preDecimalLength, 0)) +
undecimaledAmount.substring(preDecimalLength)

const trimmedPostDecimalCharacters = trimTrailingZeros
? postDecimalCharacters.replace(/0*$/, "")
: postDecimalCharacters

const decimalString =
trimmedPostDecimalCharacters.length > 0
? `.${trimmedPostDecimalCharacters}`
: ""

return `${preDecimalCharacters}${decimalString}`
}

0 comments on commit 9564273

Please sign in to comment.