Skip to content

Commit

Permalink
chore(wallet-mobile): wiring up
Browse files Browse the repository at this point in the history
  • Loading branch information
stackchain committed May 4, 2024
1 parent 4f6c79b commit c8f6475
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {amountFormatter, infoExtractName, isNft} from '@yoroi/portfolio'
import {amountFormatter, infoExtractName, isNft, isPrimaryToken} from '@yoroi/portfolio'
import {useTheme} from '@yoroi/theme'
import {Chain, Portfolio} from '@yoroi/types'
import {Swap} from '@yoroi/types'
Expand Down Expand Up @@ -39,9 +39,9 @@ export const TokenAmountItem = ({
const priceImpactRiskTheme = usePriceImpactRiskTheme(priceImpactRisk ?? 'none')

const {info} = amount
const isPrimary = info.nature === 'primary'
const name = infoExtractName(info)
const isPrimary = isPrimaryToken(info)
const detail = isPrimary ? info.description : info.fingerprint
const name = infoExtractName(info)

const formattedQuantity = !isPrivacyOff ? amountFormatter()(amount) : privacyPlaceholder

Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,85 @@
import {parseInputToBigInt, splitBigInt} from '@yoroi/common'
import {isPrimaryToken} from '@yoroi/portfolio'
import {useTheme} from '@yoroi/theme'
import {useTransfer} from '@yoroi/transfer'
import {Balance} from '@yoroi/types'
import * as React from 'react'
import {ScrollView, StyleSheet, Text, TouchableOpacity, View, ViewProps} from 'react-native'

import {Button, KeyboardAvoidingView, Spacer, TextInput} from '../../../../../components'
import {AmountItem} from '../../../../../components/AmountItem/AmountItem'
import {selectFtOrThrow} from '../../../../../yoroi-wallets/cardano/utils'
import {useTokenInfo} from '../../../../../yoroi-wallets/hooks'
import {useLanguage} from '../../../../../i18n'
import {Logger} from '../../../../../yoroi-wallets/logging'
import {asQuantity, editedFormatter, pastedFormatter, Quantities} from '../../../../../yoroi-wallets/utils'
import {editedFormatter, pastedFormatter} from '../../../../../yoroi-wallets/utils'
import {usePortfolioBalances} from '../../../../Portfolio/common/hooks/usePortfolioBalances'
import {usePortfolioPrimaryBreakdown} from '../../../../Portfolio/common/hooks/usePortfolioPrimaryBreakdown'
import {TokenAmountItem} from '../../../../Portfolio/common/TokenAmountItem/TokenAmountItem'
import {useSelectedWallet} from '../../../../WalletManager/Context'
import {useNavigateTo, useOverridePreviousSendTxRoute} from '../../../common/navigation'
import {useStrings} from '../../../common/strings'
import {useTokenQuantities} from '../../../common/useTokenQuantities'
import {NoBalance} from './ShowError/NoBalance'
import {UnableToSpend} from './ShowError/UnableToSpend'

export const EditAmountScreen = () => {
const strings = useStrings()
const {styles} = useStyles()
const navigateTo = useNavigateTo()
const {selectedTokenId, amountChanged} = useTransfer()
const {available, spendable, initialQuantity} = useTokenQuantities(selectedTokenId)
const {numberLocale} = useLanguage()

const wallet = useSelectedWallet()
const tokenInfo = useTokenInfo({wallet, tokenId: selectedTokenId}, {select: selectFtOrThrow})
const isPrimary = tokenInfo.id === wallet.primaryTokenInfo.id
const balances = usePortfolioBalances({wallet})
const primaryBreakdown = usePortfolioPrimaryBreakdown({wallet})

const [quantity, setQuantity] = React.useState<Balance.Quantity>(initialQuantity)
const [inputValue, setInputValue] = React.useState<string>(
Quantities.denominated(initialQuantity, tokenInfo.decimals ?? 0),
)
const {selectedTokenId, amountChanged, allocated, selectedTargetIndex, targets} = useTransfer()

useOverridePreviousSendTxRoute(
Quantities.isZero(initialQuantity) ? 'send-select-token-from-list' : 'send-list-amounts-to-send',
)
const amount = targets[selectedTargetIndex].entry.amounts[selectedTokenId]
const initialQuantity = amount.quantity
const available =
(balances.records.get(selectedTokenId)?.quantity ?? 0n) -
(allocated.get(selectedTargetIndex)?.get(selectedTokenId) ?? 0n)
const isPrimary = isPrimaryToken(amount.info)

const [quantity, setQuantity] = React.useState(initialQuantity)
const [inputValue, setInputValue] = React.useState(splitBigInt(initialQuantity, amount.info.decimals).bn.toFormat())
const spendable = available - primaryBreakdown.lockedAsStorageCost

useOverridePreviousSendTxRoute(initialQuantity === 0n ? 'send-select-token-from-list' : 'send-list-amounts-to-send')

React.useEffect(() => {
setQuantity(initialQuantity)
setInputValue(Quantities.denominated(initialQuantity, tokenInfo.decimals ?? 0))
}, [initialQuantity, tokenInfo.decimals])
setInputValue(splitBigInt(initialQuantity, amount.info.decimals).bn.toFormat())
}, [amount.info.decimals, initialQuantity])

const hasBalance = !Quantities.isGreaterThan(quantity, available)
const isUnableToSpend = isPrimary && Quantities.isGreaterThan(quantity, spendable)
const isZero = Quantities.isZero(quantity)
const hasBalance = available >= quantity
// primary can have locked amount
const isUnableToSpend = isPrimary && quantity > spendable
const isZero = quantity === 0n

const onChangeQuantity = (text: string) => {
try {
const quantity = asQuantity(text.length > 0 ? text : '0')
setInputValue(text)
setQuantity(Quantities.integer(quantity, tokenInfo.decimals ?? 0))
const [newInputValue, newQuantity] = parseInputToBigInt({
input: text,
decimalPlaces: amount.info.decimals,
format: numberLocale,
})
setInputValue(newInputValue)
setQuantity(newQuantity)
} catch (error) {
Logger.error('EditAmountScreen::onChangeQuantity', error)
}
}
const onMaxBalance = () => {
setInputValue(Quantities.denominated(spendable, tokenInfo.decimals ?? 0))
setQuantity(spendable)
const [newInputValue, newQuantity] = parseInputToBigInt({
input: spendable.toString(),
decimalPlaces: amount.info.decimals,
format: numberLocale,
})
setInputValue(newInputValue)
setQuantity(newQuantity)
}
const onApply = () => {
amountChanged(quantity)
amountChanged({
info: amount.info,
quantity,
})
navigateTo.selectedTokens()
}

Expand All @@ -70,11 +89,11 @@ export const EditAmountScreen = () => {
<ScrollView style={styles.scrollView} bounces={false}>
<Spacer height={16} />

<AmountItem amount={{quantity: available, tokenId: tokenInfo.id}} wallet={wallet} />
<TokenAmountItem amount={amount} isPrivacyOff network={wallet.network} privacyPlaceholder="" />

<Spacer height={40} />

<AmountInput onChange={onChangeQuantity} value={inputValue} ticker={tokenInfo.ticker} />
<AmountInput onChange={onChangeQuantity} value={inputValue} ticker={amount.info.ticker} />

<Center>
{/* {isPrimary && <PairedBalance amount={{tokenId: tokenInfo.id, quantity}} />} */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {useNavigation} from '@react-navigation/native'
import {isNft} from '@yoroi/portfolio'
import {useTheme} from '@yoroi/theme'
import {useTransfer} from '@yoroi/transfer'
import type {Balance, Portfolio} from '@yoroi/types'
import {Portfolio} from '@yoroi/types'
import * as React from 'react'
import {useLayoutEffect} from 'react'
import {defineMessages, useIntl} from 'react-intl'
Expand All @@ -11,14 +11,12 @@ import {FlatList} from 'react-native-gesture-handler'
import {useMutation} from 'react-query'

import {Boundary, Button, Icon, Spacer} from '../../../../components'
import {AmountItem} from '../../../../components/AmountItem/AmountItem'
import globalMessages from '../../../../i18n/global-messages'
import {assetsToSendProperties} from '../../../../metrics/helpers'
import {useMetrics} from '../../../../metrics/metricsManager'
import {useSearch} from '../../../../Search/SearchContext'
import {useTokenInfo} from '../../../../yoroi-wallets/hooks'
import {YoroiEntry} from '../../../../yoroi-wallets/types'
import {Amounts} from '../../../../yoroi-wallets/utils'
import {TokenAmountItem} from '../../../Portfolio/common/TokenAmountItem/TokenAmountItem'
import {useSelectedWallet} from '../../../WalletManager/Context'
import {useNavigateTo, useOverridePreviousSendTxRoute} from '../../common/navigation'
import {toYoroiEntry} from '../../common/toYoroiEntry'
Expand Down Expand Up @@ -94,14 +92,14 @@ export const ListAmountsToSendScreen = () => {
return (
<View style={styles.container}>
<AmountsList
data={tokens}
renderItem={({item: {id}}: {item: Balance.TokenInfo}) => (
data={Object.values(amounts)}
renderItem={({item: amount}) => (
<Boundary>
<ActionableAmount amount={Amounts.getAmount(amounts, id)} onRemove={onRemove} onEdit={onEdit} />
<ActionableAmount amount={amount} onRemove={onRemove} onEdit={onEdit} />
</Boundary>
)}
bounces={false}
keyExtractor={({id}) => id}
keyExtractor={({info}) => info.id}
testID="selectedTokens"
/>

Expand All @@ -126,24 +124,22 @@ export const ListAmountsToSendScreen = () => {
}

type ActionableAmountProps = {
amount: Balance.Amount
onEdit(tokenId: string): void
onRemove(tokenId: string): void
amount: Portfolio.Token.Amount
onEdit(tokenId: Portfolio.Token.Id): void
onRemove(tokenId: Portfolio.Token.Id): void
}
const ActionableAmount = ({amount, onRemove, onEdit}: ActionableAmountProps) => {
const wallet = useSelectedWallet()
const {styles} = useStyles()
const {tokenId} = amount
const tokenInfo = useTokenInfo({wallet, tokenId})

const handleRemove = () => onRemove(tokenId)
const handleEdit = () => (tokenInfo.kind === 'nft' ? null : onEdit(tokenId))
const handleRemove = () => onRemove(amount.info.id)
const handleEdit = () => (isNft(amount.info) ? null : onEdit(amount.info.id))

return (
<View style={styles.amountItem} testID="amountItem">
<Left>
<EditAmountButton onPress={handleEdit}>
<AmountItem amount={amount} wallet={wallet} />
<TokenAmountItem amount={amount} privacyPlaceholder="" network={wallet.network} isPrivacyOff />
</EditAmountButton>
</Left>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"defaultMessage": "!!!Add token",
"file": "src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.tsx",
"start": {
"line": 195,
"line": 187,
"column": 12,
"index": 6152
"index": 5953
},
"end": {
"line": 198,
"line": 190,
"column": 3,
"index": 6229
"index": 6030
}
}
]
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export * from './utils/strings'
export * from './numbers/to-bigint'
export * from './numbers/split-bigint'
export * from './numbers/bigint-formatter'
export * from './numbers/parse-input-to-bigint'

export * from './observer/observer'

Expand Down
119 changes: 119 additions & 0 deletions packages/common/src/numbers/parse-input-to-bigint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import BigNumber from 'bignumber.js'

import {parseInputToBigInt} from './parse-input-to-bigint'

describe('parse-input-to-bigint', () => {
it('parseInputToBigInt', () => {
const english = {
prefix: '',
decimalSeparator: '.',
groupSeparator: ',',
groupSize: 3,
secondaryGroupSize: 0,
fractionGroupSize: 0,
fractionGroupSeparator: ' ',
suffix: '',
}

const italian = {
...english,
decimalSeparator: ',',
groupSeparator: ' ',
}

BigNumber.config({
FORMAT: italian,
})

expect(
parseInputToBigInt({input: '', decimalPlaces: 3, format: italian}),
).toEqual(['', 0n])
expect(
parseInputToBigInt({input: ',', decimalPlaces: 3, format: italian}),
).toEqual(['0,', 0n])
expect(
parseInputToBigInt({input: '1', decimalPlaces: 3, format: italian}),
).toEqual(['1', 1000n])
expect(
parseInputToBigInt({input: '123,55', decimalPlaces: 3, format: italian}),
).toEqual(['123,55', 123550n])
expect(
parseInputToBigInt({
input: '1234,6666',
decimalPlaces: 3,
format: italian,
}),
).toEqual(['1 234,666', 1234666n])
expect(
parseInputToBigInt({input: '55,', decimalPlaces: 3, format: italian}),
).toEqual(['55,', 55000n])
expect(
parseInputToBigInt({input: '55,0', decimalPlaces: 3, format: italian}),
).toEqual(['55,0', 55000n])
expect(
parseInputToBigInt({input: '55,10', decimalPlaces: 3, format: italian}),
).toEqual(['55,10', 55100n])
expect(
parseInputToBigInt({
input: 'ab1.5c,6.5',
decimalPlaces: 3,
format: italian,
}),
).toEqual(['15,65', 15650n])

BigNumber.config({FORMAT: english})

expect(
parseInputToBigInt({input: '', decimalPlaces: 3, format: english}),
).toEqual(['', 0n])
expect(
parseInputToBigInt({input: '1', decimalPlaces: 3, format: english}),
).toEqual(['1', 1000n])
expect(
parseInputToBigInt({input: '123.55', decimalPlaces: 3, format: english}),
).toEqual(['123.55', 123550n])
expect(
parseInputToBigInt({
input: '1234.6666',
decimalPlaces: 3,
format: english,
}),
).toEqual(['1,234.666', 1234666n])
expect(
parseInputToBigInt({input: '55.', decimalPlaces: 3, format: english}),
).toEqual(['55.', 55000n])
expect(
parseInputToBigInt({input: '55.0', decimalPlaces: 3, format: english}),
).toEqual(['55.0', 55000n])
expect(
parseInputToBigInt({input: '55.10', decimalPlaces: 3, format: english}),
).toEqual(['55.10', 55100n])
expect(
parseInputToBigInt({
input: 'ab1.5c,6.5',
decimalPlaces: 3,
format: english,
}),
).toEqual(['1.56', 1560n])

expect(
parseInputToBigInt({
input: '1.23456',
decimalPlaces: 0,
format: english,
precision: 3,
}),
).toEqual(['1.234', 1n])
expect(
parseInputToBigInt({
input: '1.23456',
decimalPlaces: 2,
format: english,
precision: 3,
}),
// how? simple, precision of 3 keep 1.234, 2 decimals drop the 4, toFixed 0
// 123 with 2 decimals = 1.23
// so be mindfull when using precision and decimalPlaces together
).toEqual(['1.234', 123n])
})
})

0 comments on commit c8f6475

Please sign in to comment.