Skip to content

Commit ca0adc4

Browse files
committed
Implement max button function on TradeCreateScene
1 parent 76f668b commit ca0adc4

File tree

8 files changed

+347
-174
lines changed

8 files changed

+347
-174
lines changed

src/components/scenes/TradeCreateScene.tsx

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { useDispatch, useSelector } from '../../types/reactRedux'
2424
import type { BuyTabSceneProps, NavigationBase } from '../../types/routerTypes'
2525
import type { GuiFiatType } from '../../types/types'
2626
import { getCurrencyCode } from '../../util/CurrencyInfoHelpers'
27-
import { DECIMAL_PRECISION } from '../../util/utils'
27+
import { DECIMAL_PRECISION, mulToPrecision } from '../../util/utils'
2828
import { DropDownInputButton } from '../buttons/DropDownInputButton'
2929
import { PillButton } from '../buttons/PillButton'
3030
import { AlertCardUi4 } from '../cards/AlertCard'
@@ -69,6 +69,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
6969
const [lastUsedInput, setLastUsedInput] = useState<'fiat' | 'crypto' | null>(
7070
null
7171
)
72+
const [isMaxAmount, setIsMaxAmount] = useState(false)
7273

7374
// Selected currencies
7475
const defaultFiat = useSelector(state => getDefaultFiat(state))
@@ -194,7 +195,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
194195
selectedWallet == null ||
195196
selectedCryptoCurrencyCode == null ||
196197
lastUsedInput == null ||
197-
userInput === '' ||
198+
(userInput === '' && !isMaxAmount) ||
198199
countryCode === ''
199200
) {
200201
return null
@@ -205,7 +206,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
205206
pluginId: selectedWallet.currencyInfo.pluginId,
206207
tokenId: selectedCrypto?.tokenId ?? null,
207208
displayCurrencyCode: selectedCryptoCurrencyCode,
208-
exchangeAmount: userInput,
209+
exchangeAmount: isMaxAmount ? { max: true } : userInput,
209210
fiatCurrencyCode: selectedFiatCurrencyCode,
210211
amountType: lastUsedInput,
211212
direction: 'buy',
@@ -219,6 +220,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
219220
selectedCryptoCurrencyCode,
220221
selectedCrypto,
221222
userInput,
223+
isMaxAmount,
222224
selectedFiatCurrencyCode,
223225
lastUsedInput,
224226
countryCode,
@@ -285,7 +287,9 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
285287
if (fiatAmt === '' || quoteExchangeRate === 0) return ''
286288

287289
const decimals =
288-
denomination?.multiplier.match(/0/g)?.length ?? DECIMAL_PRECISION
290+
denomination != null
291+
? mulToPrecision(denomination.multiplier)
292+
: DECIMAL_PRECISION
289293
try {
290294
return div(fiatAmt, quoteExchangeRate.toString(), decimals)
291295
} catch {
@@ -297,6 +301,9 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
297301

298302
// Derived state for display values
299303
const displayFiatAmount = React.useMemo(() => {
304+
if (isMaxAmount && bestQuote != null) {
305+
return bestQuote.fiatAmount
306+
}
300307
if (userInput === '' || lastUsedInput === null) return ''
301308

302309
if (lastUsedInput === 'fiat') {
@@ -305,9 +312,12 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
305312
// User entered crypto, convert to fiat only if we have a quote
306313
return convertCryptoToFiat(userInput)
307314
}
308-
}, [userInput, lastUsedInput, convertCryptoToFiat])
315+
}, [userInput, lastUsedInput, convertCryptoToFiat, isMaxAmount, bestQuote])
309316

310317
const displayCryptoAmount = React.useMemo(() => {
318+
if (isMaxAmount && bestQuote != null) {
319+
return bestQuote.cryptoAmount
320+
}
311321
if (userInput === '' || lastUsedInput === null) return ''
312322

313323
if (lastUsedInput === 'crypto') {
@@ -316,7 +326,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
316326
// User entered fiat, convert to crypto only if we have a quote
317327
return convertFiatToCrypto(userInput)
318328
}
319-
}, [userInput, lastUsedInput, convertFiatToCrypto])
329+
}, [userInput, lastUsedInput, convertFiatToCrypto, isMaxAmount, bestQuote])
320330

321331
//
322332
// Handlers
@@ -375,7 +385,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
375385
selectedWallet == null ||
376386
selectedCryptoCurrencyCode == null ||
377387
lastUsedInput == null ||
378-
userInput === '' ||
388+
(userInput === '' && !isMaxAmount) ||
379389
rampQuoteRequest == null
380390
) {
381391
return
@@ -396,17 +406,29 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
396406
}, [selectedCryptoCurrencyCode, quoteExchangeRate, selectedFiatCurrencyCode])
397407

398408
const handleFiatChangeText = useHandler((text: string) => {
409+
setIsMaxAmount(false)
399410
setUserInput(text)
400411
setLastUsedInput('fiat')
401412
})
402413

403414
const handleCryptoChangeText = useHandler((text: string) => {
415+
setIsMaxAmount(false)
404416
setUserInput(text)
405417
setLastUsedInput('crypto')
406418
})
407419

408420
const handleMaxPress = useHandler(() => {
409-
// TODO: Implement max functionality
421+
if (isMaxAmount) {
422+
// Toggle off max mode
423+
setIsMaxAmount(false)
424+
setUserInput('')
425+
// Keep lastUsedInput as is so the UI remains focused on the same field
426+
} else {
427+
// Toggle on max mode
428+
setIsMaxAmount(true)
429+
setLastUsedInput('fiat')
430+
setUserInput('') // Clear input when using max
431+
}
410432
})
411433

412434
// Render region selection view
@@ -531,9 +553,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
531553
{/* Bottom Input (Crypto by design) */}
532554
<InputRow>
533555
<DropDownInputButton onPress={handleCryptDropdown}>
534-
{selectedCrypto == null || selectedWallet == null ? (
535-
<FlagIcon sizeRem={1.5} source={{ uri: '' }} />
536-
) : (
556+
{selectedCrypto == null || selectedWallet == null ? null : (
537557
<CryptoIcon
538558
sizeRem={1.5}
539559
pluginId={selectedWallet?.currencyInfo.pluginId ?? ''}
@@ -566,7 +586,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
566586
{selectedCrypto == null ||
567587
selectedWallet == null ||
568588
denomination == null ||
569-
userInput === '' ||
589+
(userInput === '' && !isMaxAmount) ||
570590
lastUsedInput == null ||
571591
(!isLoadingQuotes && sortedQuotes.length === 0) ? null : (
572592
<ExchangeRateContainer>
@@ -608,7 +628,8 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
608628
supportedPluginsError != null &&
609629
quoteErrors.length > 0 &&
610630
sortedQuotes.length === 0 &&
611-
supportedPlugins.length > 0 ? (
631+
supportedPlugins.length > 0 &&
632+
(userInput !== '' || isMaxAmount) ? (
612633
<AlertCardUi4
613634
type="error"
614635
title={lstrings.trade_buy_unavailable_title}
@@ -629,7 +650,7 @@ export const TradeCreateScene: React.FC<Props> = (props: Props) => {
629650
disabled={
630651
selectedWallet == null ||
631652
selectedCryptoCurrencyCode == null ||
632-
userInput === '' ||
653+
(userInput === '' && !isMaxAmount) ||
633654
lastUsedInput === null ||
634655
isPluginsLoading ||
635656
isCheckingSupport ||
@@ -662,10 +683,16 @@ const InputRow = styled(View)(theme => ({
662683
gap: theme.rem(1)
663684
}))
664685

665-
const MaxButton = styled(EdgeTouchableOpacity)(theme => ({
666-
alignSelf: 'flex-end',
667-
padding: theme.rem(0.5)
668-
}))
686+
const MaxButton = styled(EdgeTouchableOpacity)<{ active?: boolean }>(
687+
theme => props => ({
688+
alignSelf: 'flex-end',
689+
padding: theme.rem(0.25),
690+
margin: theme.rem(0.25),
691+
borderWidth: 1,
692+
borderRadius: theme.rem(0.5),
693+
borderColor: props.active === true ? theme.escapeButtonText : 'transparent'
694+
})
695+
)
669696

670697
const MaxButtonText = styled(Text)(theme => ({
671698
color: theme.escapeButtonText,

src/plugins/ramps/banxa/banxaRampPlugin.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -853,14 +853,18 @@ export const banxaRampPlugin: RampPluginFactory = (
853853
const {
854854
direction,
855855
regionCode,
856-
exchangeAmount,
857856
amountType,
858857
pluginId: currencyPluginId,
859858
fiatCurrencyCode,
860859
displayCurrencyCode,
861860
tokenId
862861
} = request
863862

863+
const isMaxAmount =
864+
typeof request.exchangeAmount === 'object' && request.exchangeAmount.max
865+
const exchangeAmount =
866+
typeof request.exchangeAmount === 'object' ? '' : request.exchangeAmount
867+
864868
// Fetch provider configuration (cached or fresh)
865869
const config = await fetchProviderConfig()
866870
const { allowedCountryCodes, allowedCurrencyCodes, banxaPaymentsMap } =
@@ -978,12 +982,44 @@ export const banxaRampPlugin: RampPluginFactory = (
978982
payment_method_id: paymentObj.id
979983
}
980984

985+
let maxAmountString = ''
986+
if (isMaxAmount) {
987+
if (amountType === 'fiat') {
988+
maxAmountString = paymentObj.max
989+
} else {
990+
// For crypto, we need to fetch a quote with max fiat to get the crypto amount
991+
const maxFiatQueryParams: any = {
992+
account_reference: username,
993+
payment_method_id: paymentObj.id,
994+
source: direction === 'buy' ? fiatCode : banxaCoin,
995+
target: direction === 'buy' ? banxaCoin : fiatCode
996+
}
997+
if (direction === 'buy') {
998+
maxFiatQueryParams.source_amount = paymentObj.max
999+
} else {
1000+
maxFiatQueryParams.target_amount = paymentObj.max
1001+
}
1002+
const maxResponse = await banxaFetch({
1003+
method: 'GET',
1004+
url: apiUrl,
1005+
hmacUser,
1006+
path: 'api/prices',
1007+
apiKey,
1008+
queryParams: maxFiatQueryParams
1009+
})
1010+
const maxPrices = asBanxaPricesResponse(maxResponse)
1011+
maxAmountString = maxPrices.data.prices[0].coin_amount
1012+
}
1013+
}
1014+
9811015
if (direction === 'buy') {
9821016
queryParams.source = fiatCode
9831017
queryParams.target = banxaCoin
9841018
if (amountType === 'fiat') {
985-
queryParams.source_amount = exchangeAmount
986-
if (!checkMinMax(exchangeAmount, paymentObj)) {
1019+
queryParams.source_amount = isMaxAmount
1020+
? maxAmountString
1021+
: exchangeAmount
1022+
if (!isMaxAmount && !checkMinMax(exchangeAmount, paymentObj)) {
9871023
if (gt(exchangeAmount, paymentObj.max)) {
9881024
throw new FiatProviderError({
9891025
providerId: pluginId,
@@ -999,16 +1035,21 @@ export const banxaRampPlugin: RampPluginFactory = (
9991035
displayCurrencyCode: fiatCurrencyCode
10001036
})
10011037
}
1038+
continue
10021039
}
10031040
} else {
1004-
queryParams.target_amount = exchangeAmount
1041+
queryParams.target_amount = isMaxAmount
1042+
? maxAmountString
1043+
: exchangeAmount
10051044
}
10061045
} else {
10071046
queryParams.source = banxaCoin
10081047
queryParams.target = fiatCode
10091048
if (amountType === 'fiat') {
1010-
queryParams.target_amount = exchangeAmount
1011-
if (!checkMinMax(exchangeAmount, paymentObj)) {
1049+
queryParams.target_amount = isMaxAmount
1050+
? maxAmountString
1051+
: exchangeAmount
1052+
if (!isMaxAmount && !checkMinMax(exchangeAmount, paymentObj)) {
10121053
if (gt(exchangeAmount, paymentObj.max)) {
10131054
throw new FiatProviderError({
10141055
providerId: pluginId,
@@ -1024,9 +1065,12 @@ export const banxaRampPlugin: RampPluginFactory = (
10241065
displayCurrencyCode: fiatCurrencyCode
10251066
})
10261067
}
1068+
continue
10271069
}
10281070
} else {
1029-
queryParams.source_amount = exchangeAmount
1071+
queryParams.source_amount = isMaxAmount
1072+
? maxAmountString
1073+
: exchangeAmount
10301074
}
10311075
}
10321076

@@ -1252,7 +1296,7 @@ export const banxaRampPlugin: RampPluginFactory = (
12521296
onClose: () => {
12531297
clearInterval(interval)
12541298
},
1255-
onUrlChange: async (changeUrl: string) => {
1299+
onUrlChange: async (changeUrl: string): Promise<void> => {
12561300
console.log(`onUrlChange url=${changeUrl}`)
12571301
if (changeUrl === RETURN_URL_SUCCESS) {
12581302
clearInterval(interval)

0 commit comments

Comments
 (0)