mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
more progress, math still broken though
This commit is contained in:
Vendored
+3
-7
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"[javascriptreact][typescriptreact]": {
|
||||
"[javascriptreact][typescriptreact][javascript][typescript]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[javascript][typescript]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
"editor.tabSize": 4
|
||||
}
|
||||
}
|
||||
@@ -1,121 +1,179 @@
|
||||
import React from 'react'
|
||||
import { View, ViewStyle, TextInput } from 'react-native'
|
||||
import { AmountInput } from "./AmountInput"
|
||||
import { CurrencyAmount } from '../screens/Wallet/CurrencyAmount'
|
||||
import { Text, Icon } from './'
|
||||
import { spacing, useThemeColor } from '../theme'
|
||||
import { verticalScale } from '@gocodingnow/rn-size-matters'
|
||||
import { TransactionStatus } from '../models/Transaction'
|
||||
import { MintUnit, CurrencyCode, getCurrency, convertToFromSats } from '../services/wallet/currency'
|
||||
import React, { useState } from 'react'
|
||||
import { TextInput, TextStyle, TouchableOpacity, View, ViewStyle } from 'react-native'
|
||||
import { useStores } from '../models'
|
||||
import type { Mint } from '../models/Mint'
|
||||
import { TransactionStatus } from '../models/Transaction'
|
||||
import { MintHeader } from '../screens/Mints/MintHeader'
|
||||
import { CurrencyAmount } from '../screens/Wallet/CurrencyAmount'
|
||||
import { convertToFromSats, convertToSatsFrom, CurrencyCode, getCurrency, getCurrencyByCode, MintUnit } from '../services/wallet/currency'
|
||||
import { spacing, useThemeColor } from '../theme'
|
||||
import { round, toNumber } from '../utils/number'
|
||||
import { AmountInput, Icon, Text } from './index'
|
||||
import { log } from '../services'
|
||||
|
||||
interface IAmountInputHeaderProps {
|
||||
amountInputRef: React.RefObject<TextInput>
|
||||
amountToSend: string
|
||||
setAmountToSend: (amount: string) => void
|
||||
unit: MintUnit
|
||||
onAmountEndEditing?: () => void
|
||||
transactionStatus?: TransactionStatus
|
||||
isCashuPrWithAmount?: boolean
|
||||
lockedPubkey?: string
|
||||
unitRef: React.RefObject<MintUnit>
|
||||
amountInputRef: React.RefObject<TextInput>
|
||||
amountToSend: string
|
||||
setAmountToSend: (amount: string) => void
|
||||
unit: MintUnit
|
||||
onAmountEndEditing?: () => void
|
||||
transactionStatus?: TransactionStatus
|
||||
isCashuPrWithAmount?: boolean
|
||||
lockedPubkey?: string
|
||||
unitRef: React.RefObject<MintUnit>
|
||||
mintHeaderMint: Mint
|
||||
}
|
||||
|
||||
const $amountContainer: ViewStyle = {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}
|
||||
|
||||
const $headerContainer: TextStyle = {
|
||||
alignItems: 'center',
|
||||
padding: spacing.extraSmall,
|
||||
paddingTop: 0,
|
||||
height: spacing.screenHeight * 0.30,
|
||||
}
|
||||
|
||||
const $pubKey: ViewStyle = {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: -spacing.extraSmall
|
||||
}
|
||||
|
||||
export function AmountInputHeader(props: IAmountInputHeaderProps) {
|
||||
const { userSettingsStore, walletStore } = useStores()
|
||||
const { userSettingsStore, walletStore } = useStores()
|
||||
const [isFiatMode, setIsFiatMode] = useState(false)
|
||||
|
||||
const amountInputColor = useThemeColor('amountInput');
|
||||
const convertedAmountColor = useThemeColor('headerSubTitle');
|
||||
const amountInputColor = useThemeColor('amountInput');
|
||||
const convertedAmountColor = useThemeColor('headerSubTitle');
|
||||
const headerBg = useThemeColor('header')
|
||||
|
||||
const {
|
||||
amountInputRef,
|
||||
amountToSend,
|
||||
setAmountToSend,
|
||||
unit,
|
||||
onAmountEndEditing,
|
||||
transactionStatus,
|
||||
isCashuPrWithAmount,
|
||||
lockedPubkey,
|
||||
unitRef
|
||||
} = props
|
||||
const {
|
||||
amountInputRef,
|
||||
amountToSend,
|
||||
setAmountToSend,
|
||||
unit,
|
||||
onAmountEndEditing,
|
||||
transactionStatus,
|
||||
isCashuPrWithAmount,
|
||||
lockedPubkey,
|
||||
unitRef,
|
||||
mintHeaderMint
|
||||
} = props
|
||||
|
||||
const isConvertedAmountVisible = () => {
|
||||
return (
|
||||
walletStore.exchangeRate &&
|
||||
(userSettingsStore.exchangeCurrency === getCurrency(unit).code ||
|
||||
unit === 'sat') &&
|
||||
getConvertedAmount() !== undefined
|
||||
)
|
||||
}
|
||||
const fiatCurrency = userSettingsStore.exchangeCurrency
|
||||
const isFiatSupported = fiatCurrency === CurrencyCode.USD || fiatCurrency === CurrencyCode.EUR
|
||||
const canUseFiatMode = isFiatSupported && walletStore.exchangeRate && unit === 'sat'
|
||||
|
||||
const getConvertedAmount = () => {
|
||||
if (!walletStore.exchangeRate) return undefined;
|
||||
const getFiatUnit = () => {
|
||||
const currencyData = getCurrencyByCode(fiatCurrency)
|
||||
return currencyData?.mintUnit || 'sat'
|
||||
}
|
||||
|
||||
const precision = getCurrency(unitRef.current).precision
|
||||
return convertToFromSats(
|
||||
round(toNumber(amountToSend) * precision, 0) || 0,
|
||||
getCurrency(unitRef.current).code,
|
||||
walletStore.exchangeRate
|
||||
)
|
||||
}
|
||||
// Convert FIAT amount to display units (e.g., sats to mBTC)
|
||||
const FIATtoSATS = (inputAmount: string) => {
|
||||
if (!walletStore.exchangeRate) return undefined;
|
||||
log.trace("er:", walletStore.exchangeRate)
|
||||
|
||||
return <View style={$amountContainer}>
|
||||
<AmountInput
|
||||
ref={amountInputRef}
|
||||
value={amountToSend}
|
||||
onChangeText={amount => setAmountToSend(amount)}
|
||||
unit={unit}
|
||||
onEndEditing={transactionStatus !== TransactionStatus.PENDING ? onAmountEndEditing : undefined}
|
||||
editable={!(transactionStatus === TransactionStatus.PENDING || isCashuPrWithAmount)}
|
||||
style={{ color: amountInputColor }}
|
||||
/>
|
||||
{isConvertedAmountVisible() && (
|
||||
<CurrencyAmount
|
||||
amount={getConvertedAmount() ?? 0}
|
||||
currencyCode={unit === 'sat' ? userSettingsStore.exchangeCurrency : CurrencyCode.SAT}
|
||||
symbolStyle={{ color: convertedAmountColor, marginTop: spacing.tiny, fontSize: verticalScale(10) }}
|
||||
amountStyle={{ color: convertedAmountColor, lineHeight: spacing.medium }}
|
||||
size='medium'
|
||||
containerStyle={{ justifyContent: 'center' }}
|
||||
/>
|
||||
)}
|
||||
{lockedPubkey ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: isConvertedAmountVisible() ? -spacing.extraSmall : undefined
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="faLock"
|
||||
size={spacing.small}
|
||||
color={amountInputColor}
|
||||
/>
|
||||
<Text
|
||||
size='xs'
|
||||
tx="sendLocked"
|
||||
style={{ color: amountInputColor, marginLeft: spacing.tiny }}
|
||||
const precision = getCurrency(unitRef.current).precision
|
||||
return convertToSatsFrom(
|
||||
round(toNumber(inputAmount) * precision, 0) || 0,
|
||||
getCurrency(unitRef.current).code,
|
||||
walletStore.exchangeRate
|
||||
)
|
||||
}
|
||||
|
||||
// Convert display units to FIAT amount
|
||||
const SATStoFIAT = (inputAmount: string) => {
|
||||
if (!walletStore.exchangeRate) return undefined;
|
||||
log.trace("er:", walletStore.exchangeRate)
|
||||
|
||||
const precision = getCurrency(unitRef.current).precision
|
||||
return convertToFromSats(
|
||||
round(toNumber(inputAmount) * precision, 0) || 0,
|
||||
getCurrency(unitRef.current).code,
|
||||
walletStore.exchangeRate
|
||||
)
|
||||
}
|
||||
|
||||
const isConvertedAmountVisible = () => {
|
||||
return canUseFiatMode && walletStore.exchangeRate
|
||||
}
|
||||
|
||||
const handleAmountChange = (amount: string) => {
|
||||
if (isFiatMode) {
|
||||
setAmountToSend(FIATtoSATS(amount).toString())
|
||||
} else {
|
||||
setAmountToSend(amount)
|
||||
}
|
||||
}
|
||||
|
||||
return <View style={[$headerContainer, { backgroundColor: headerBg }]}>
|
||||
<MintHeader
|
||||
mint={mintHeaderMint}
|
||||
unit={isFiatMode && canUseFiatMode ? getFiatUnit() : unitRef.current}
|
||||
/>
|
||||
<View style={$amountContainer}>
|
||||
{isFiatMode && canUseFiatMode ? (
|
||||
<AmountInput
|
||||
value={SATStoFIAT(amountToSend).toString()}
|
||||
onChangeText={amount => setAmountToSend(FIATtoSATS(amount).toString())}
|
||||
unit={getFiatUnit()}
|
||||
onEndEditing={transactionStatus !== TransactionStatus.PENDING ? onAmountEndEditing : undefined}
|
||||
style={{ color: amountInputColor }}
|
||||
/>
|
||||
) : (
|
||||
<AmountInput
|
||||
ref={amountInputRef}
|
||||
value={amountToSend}
|
||||
onChangeText={handleAmountChange}
|
||||
unit={unit}
|
||||
onEndEditing={transactionStatus !== TransactionStatus.PENDING ? onAmountEndEditing : undefined}
|
||||
editable={!(transactionStatus === TransactionStatus.PENDING || isCashuPrWithAmount)}
|
||||
style={{ color: amountInputColor }}
|
||||
/>
|
||||
)}
|
||||
{isConvertedAmountVisible() && (
|
||||
<TouchableOpacity onPress={() => canUseFiatMode && setIsFiatMode(!isFiatMode)}>
|
||||
<CurrencyAmount
|
||||
amount={isFiatMode ? toNumber(amountToSend) * getCurrency(unitRef.current).precision : SATStoFIAT(amountToSend)}
|
||||
currencyCode={isFiatMode ? CurrencyCode.SAT : fiatCurrency}
|
||||
symbolStyle={{ color: convertedAmountColor, marginTop: spacing.tiny, fontSize: verticalScale(10) }}
|
||||
amountStyle={{ color: convertedAmountColor, lineHeight: spacing.medium }}
|
||||
size='medium'
|
||||
containerStyle={{ justifyContent: 'center' }}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{lockedPubkey ? (
|
||||
<View style={$pubKey}>
|
||||
<Icon
|
||||
icon="faLock"
|
||||
size={spacing.small}
|
||||
color={amountInputColor}
|
||||
/>
|
||||
<Text
|
||||
size='xs'
|
||||
tx="sendLocked"
|
||||
style={{ color: amountInputColor, marginLeft: spacing.tiny }}
|
||||
/>
|
||||
|
||||
</View>
|
||||
) : (
|
||||
<Text
|
||||
size='xs'
|
||||
tx='amountSend'
|
||||
style={{
|
||||
color: amountInputColor,
|
||||
textAlign: 'center',
|
||||
marginTop: isConvertedAmountVisible() ? -spacing.extraSmall : undefined
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<Text
|
||||
size='xs'
|
||||
tx='amountSend'
|
||||
style={{
|
||||
color: amountInputColor,
|
||||
textAlign: 'center',
|
||||
marginTop: isConvertedAmountVisible() ? -spacing.extraSmall : undefined
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
}
|
||||
@@ -1000,7 +1000,6 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
setError(e)
|
||||
}
|
||||
|
||||
const headerBg = useThemeColor('header')
|
||||
const hintColor = useThemeColor('textDim')
|
||||
const inputText = useThemeColor('text')
|
||||
const inputBg = useThemeColor('background')
|
||||
@@ -1032,12 +1031,7 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
|
||||
return (
|
||||
<Screen preset="fixed" contentContainerStyle={$screen}>
|
||||
<MintHeader
|
||||
mint={mintBalanceToSendFrom ? mintsStore.findByUrl(mintBalanceToSendFrom?.mintUrl) : undefined}
|
||||
unit={unitRef.current}
|
||||
/>
|
||||
<View style={[$headerContainer, { backgroundColor: headerBg}]}>
|
||||
<AmountInputHeader
|
||||
<AmountInputHeader
|
||||
amountInputRef={amountInputRef}
|
||||
amountToSend={amountToSend}
|
||||
setAmountToSend={setAmountToSend}
|
||||
@@ -1047,8 +1041,8 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
isCashuPrWithAmount={isCashuPrWithAmount}
|
||||
lockedPubkey={lockedPubkey}
|
||||
unitRef={unitRef}
|
||||
/>
|
||||
</View>
|
||||
mintHeaderMint={mintBalanceToSendFrom ? mintsStore.findByUrl(mintBalanceToSendFrom?.mintUrl) : undefined}
|
||||
/>
|
||||
<View style={$contentContainer}>
|
||||
{!encodedTokenToSend && (
|
||||
<MemoInputCard
|
||||
@@ -1766,14 +1760,6 @@ const $screen: ViewStyle = {
|
||||
flex: 1,
|
||||
}
|
||||
|
||||
const $headerContainer: TextStyle = {
|
||||
alignItems: 'center',
|
||||
padding: spacing.extraSmall,
|
||||
paddingTop: 0,
|
||||
height: spacing.screenHeight * 0.20,
|
||||
|
||||
}
|
||||
|
||||
const $pubkeyInput: TextStyle = {
|
||||
flex: 1,
|
||||
// borderRadius: 0,
|
||||
@@ -1785,16 +1771,6 @@ const $pubkeyInput: TextStyle = {
|
||||
// borderWidth: 1,
|
||||
}
|
||||
|
||||
const $amountInput: TextStyle = {
|
||||
borderRadius: spacing.small,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: verticalScale(48),
|
||||
fontFamily: typography.primary?.medium,
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
const $contentContainer: TextStyle = {
|
||||
flex: 1,
|
||||
padding: spacing.extraSmall,
|
||||
|
||||
@@ -155,6 +155,15 @@ export const getCurrency = (unit: MintUnit) => {
|
||||
return currencyData as CurrencyData
|
||||
}
|
||||
|
||||
export const getCurrencyByCode = (code: CurrencyCode): CurrencyData | undefined => {
|
||||
for (const [currencyCode, currencyData] of Object.entries(Currencies)) {
|
||||
if (currencyCode === code && currencyData) {
|
||||
return currencyData satisfies CurrencyData
|
||||
}
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
export const convertToFromSats = (amount: number, currencyFrom: CurrencyCode, satExchangeRate: ExchangeRate) => {
|
||||
// exchangeRate is always 1 fiat precision unit (cent) in SAT {currency: 'EUR', rate: 15.69}
|
||||
|
||||
@@ -163,5 +172,15 @@ export const convertToFromSats = (amount: number, currencyFrom: CurrencyCode, sa
|
||||
}
|
||||
|
||||
return amount * satExchangeRate.rate
|
||||
}
|
||||
|
||||
export const convertToSatsFrom = (amount: number, currencyFrom: CurrencyCode, satExchangeRate: ExchangeRate) => {
|
||||
// exchangeRate is always 1 fiat precision unit (cent) in SAT {currency: 'EUR', rate: 15.69}
|
||||
|
||||
if(currencyFrom === CurrencyCode.SAT) {
|
||||
return amount * satExchangeRate.rate
|
||||
}
|
||||
|
||||
return amount / satExchangeRate.rate
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user