From 95311e619955592936b9271bce36ce36f6d654c3 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Fri, 7 Nov 2025 11:19:30 -0800 Subject: [PATCH 1/2] Refactor `KavButton` into `SceneWrapper` - Avoids duplicate inset logic and having to set a sibling component at the callsite. - Also make it more generic in preparation for other button layouts. --- package.json | 1 - src/components/buttons/KavButtons.tsx | 89 ++++ src/components/common/SceneWrapper.tsx | 121 +++++- src/components/keyboard/KavButton.tsx | 114 ----- src/components/scenes/RampCreateScene.tsx | 394 +++++++++--------- src/components/scenes/RampKycFormScene.tsx | 188 +++++---- .../gui/scenes/FiatPluginEnterAmountScene.tsx | 271 ++++++------ yarn.lock | 5 - 8 files changed, 639 insertions(+), 544 deletions(-) create mode 100644 src/components/buttons/KavButtons.tsx delete mode 100644 src/components/keyboard/KavButton.tsx diff --git a/package.json b/package.json index e9dfa668424..e5abcc64f0d 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,6 @@ "react-native-image-colors": "^2.4.0", "react-native-image-picker": "^8.2.1", "react-native-in-app-review": "^4.3.5", - "react-native-keyboard-accessory": "^0.1.16", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-keyboard-controller": "^1.19.0", "react-native-linear-gradient": "^2.8.3", diff --git a/src/components/buttons/KavButtons.tsx b/src/components/buttons/KavButtons.tsx new file mode 100644 index 00000000000..b429fc3541b --- /dev/null +++ b/src/components/buttons/KavButtons.tsx @@ -0,0 +1,89 @@ +import * as React from 'react' + +import { useHandler } from '../../hooks/useHandler' +import { BlurBackgroundNoRoundedCorners } from '../common/BlurBackground' +import { EdgeAnim, fadeInDown10 } from '../common/EdgeAnim' +import { cacheStyles, type Theme, useTheme } from '../services/ThemeContext' +import type { ButtonInfo } from './ButtonsView' +import { EdgeButton } from './EdgeButton' + +interface Props { + primary: ButtonInfo + tertiary?: ButtonInfo +} + +// Renders KAV buttons content: +// - If only primary: one full-width primary button +// - If tertiary provided: two mini buttons (tertiary left, primary right) +export const KavButtons: React.FC = props => { + const { primary, tertiary } = props + const theme = useTheme() + const styles = getStyles(theme) + + const handlePrimaryPress = useHandler(() => { + const res = primary.onPress?.() + Promise.resolve(res).catch(() => {}) + }) + const handleTertiaryPress = useHandler(() => { + const res = tertiary?.onPress?.() + Promise.resolve(res).catch(() => {}) + }) + + if (tertiary == null) { + return ( + + ) + } + + return ( + + + + + + ) +} + +const getStyles = cacheStyles((theme: Theme) => ({ + container: { + position: 'relative', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: theme.rem(0.5) + }, + tertiary: { + marginTop: theme.rem(0.25) + } +})) diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx index 99b4a4bd1e8..b8b41b68695 100644 --- a/src/components/common/SceneWrapper.tsx +++ b/src/components/common/SceneWrapper.tsx @@ -6,14 +6,21 @@ import { } from '@react-navigation/native' import * as React from 'react' import { useEffect, useMemo, useState } from 'react' -import { Keyboard, StyleSheet, View, type ViewStyle } from 'react-native' +import { + Keyboard, + Platform, + StyleSheet, + View, + type ViewStyle +} from 'react-native' import { useKeyboardHandler, useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller' import Reanimated, { useAnimatedReaction, - useAnimatedStyle + useAnimatedStyle, + useSharedValue } from 'react-native-reanimated' import { type EdgeInsets, @@ -118,6 +125,14 @@ interface SceneWrapperProps { // True to make the scene scrolling (if avoidKeyboard is false): scroll?: boolean + + // Optional "dock" view rendered at the bottom of the scene, attached to the + // keyboard, tabs, footer, etc, whatever is highest: + dockProps?: { + children: React.ReactNode + keyboardVisibleOnly?: boolean + contentContainerStyle?: ViewStyle + } } /** @@ -147,12 +162,14 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { hasTabs = false, padding = 0, renderFooter, - scroll = false + scroll = false, + dockProps } = props const notificationHeight = useSelector(state => state.ui.notificationHeight) const navigation = useNavigation() + const isIos = Platform.OS === 'ios' // We need to track this state in the JS thread because insets are not shared values const [isKeyboardOpen, setIsKeyboardOpen] = useState(false) @@ -163,6 +180,33 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { } }) + // Local keyboard opening/closing start/end state for dock parity between iOS + // and Android. `keyboardWillShow`/`keyboardDidShow` mean different things on + // each platform: + const [isKeyboardVisibleDock, setKeyboardVisibleDock] = useState(false) + // Track closing/opening state explicitly for animation direction: + const isClosingSv = useSharedValue(false) + useEffect(() => { + const showEvent = isIos ? 'keyboardWillShow' : 'keyboardDidShow' + const hideEvent = isIos ? 'keyboardWillHide' : 'keyboardDidHide' + const onShow = (): void => { + setKeyboardVisibleDock(true) + isClosingSv.value = false + } + const onHide = (): void => { + setKeyboardVisibleDock(false) + isClosingSv.value = true + } + const showListener = Keyboard.addListener(showEvent, onShow) + const hideListener = Keyboard.addListener(hideEvent, onHide) + return () => { + showListener.remove() + hideListener.remove() + } + // No need to depend on `isClosingSv` since it's a shared value + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isIos]) + // Reset the footer ratio when focused // We can do this because multiple calls to resetFooterRatio isn't costly // because it just sets snapTo SharedValue to `1` @@ -233,6 +277,22 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { [insets.top, insets.right, insets.bottom, insets.left] ) + // Collapsed bottom inset that ignores keyboard-open state (used to clamp during close): + const collapsedInsetBottom = useMemo( + () => + safeAreaInsets.bottom + + (hasNotifications ? notificationHeight : 0) + + (hasTabs ? MAX_TAB_BAR_HEIGHT : 0) + + footerHeight, + [ + footerHeight, + hasNotifications, + hasTabs, + notificationHeight, + safeAreaInsets.bottom + ] + ) + // This is a convenient styles object which may be applied to scene container // components to offset the inset styles applied to the SceneWrapper. const undoInsetStyle: UndoInsetStyle = useMemo( @@ -267,6 +327,58 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { return children }, [children, sceneWrapperInfo]) + // Build Dock View element + const keyboardVisibleOnlyDoc = dockProps?.keyboardVisibleOnly ?? true + const dockBaseStyle = useMemo( + () => ({ + position: 'absolute' as const, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'transparent' + }), + [] + ) + const insetBottomSv = useSharedValue(insets.bottom) + const collapsedInsetSv = useSharedValue(collapsedInsetBottom) + useEffect(() => { + insetBottomSv.value = insets.bottom + collapsedInsetSv.value = collapsedInsetBottom + }, [collapsedInsetBottom, insets.bottom, collapsedInsetSv, insetBottomSv]) + const dockAnimatedStyle = useAnimatedStyle(() => { + // keyboardHeightDiff.value is negative when open; invert to get height + const keyboardHeight = + keyboardHeightDiff.value < 0 ? -keyboardHeightDiff.value : 0 + const isClosing = isClosingSv.value + + let bottom: number + if (keyboardHeight > 0) { + // While opening, ignore insets to hug keyboard. + // While closing, never dip below the insets (avoid flicker under tab bar). + bottom = isClosing + ? Math.max(keyboardHeight, collapsedInsetSv.value) + : keyboardHeight + } else { + // Settled closed: rest above insets. + bottom = collapsedInsetSv.value + } + + return { bottom } + }) + const shouldShowDock = + dockProps != null && (!keyboardVisibleOnlyDoc || isKeyboardVisibleDock) + const dockElement = !shouldShowDock ? null : ( + + {dockProps?.children} + + ) + if (scroll) { return ( <> @@ -300,6 +412,7 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { navigation={navigation} /> ) : null} + {dockElement} ) @@ -342,6 +455,7 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { navigation={navigation} /> ) : null} + {dockElement} ) @@ -377,6 +491,7 @@ function SceneWrapperComponent(props: SceneWrapperProps): React.ReactElement { navigation={navigation} /> ) : null} + {dockElement} ) diff --git a/src/components/keyboard/KavButton.tsx b/src/components/keyboard/KavButton.tsx deleted file mode 100644 index 08705a43147..00000000000 --- a/src/components/keyboard/KavButton.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as React from 'react' -import { Keyboard, Platform } from 'react-native' -import { KeyboardAccessoryView } from 'react-native-keyboard-accessory' -import { useSafeAreaInsets } from 'react-native-safe-area-context' - -import { useSelector } from '../../types/reactRedux' -import { EdgeButton } from '../buttons/EdgeButton' -import { MAX_TAB_BAR_HEIGHT } from '../themed/MenuTabs' - -interface KavButtonProps { - children?: React.ReactNode - disabled?: boolean - hasNotifications?: boolean - hasTabs?: boolean - label?: string - spinner?: boolean - testID?: string - visible?: boolean - onPress?: () => void | Promise -} - -/** - * A keyboard accessory button that spans most of the width of the screen, - * positioned above the keyboard. - * - * Collapsing the keyboard will animate the button to the bottom of the scene, - * taking into account any insets from tabs, notification cards, etc. - * - * IMPORTANT: This component MUST be placed as a direct sibling of SceneWrapper - * for proper keyboard positioning. - * - * TODO: Consider moving this to SceneWrapper since there is a lot of duplicate - * inset logic - */ -export const KavButton = (props: KavButtonProps) => { - const { - children, - disabled = false, - label, - onPress, - spinner = false, - visible = true, - testID, - hasTabs = false, - hasNotifications = false - } = props - const isIos = Platform.OS === 'ios' - - const safeAreaInsets = useSafeAreaInsets() - const notificationHeight = useSelector(state => state.ui.notificationHeight) - - const [isKeyboardVisible, setKeyboardVisible] = React.useState(false) - - React.useEffect(() => { - // Listening event is different between iOS and Android, but actually mean - // the same thing: when the keyboard begins its movement - const keyboardDidShowListener = Keyboard.addListener( - isIos ? 'keyboardWillShow' : 'keyboardDidShow', - () => { - setKeyboardVisible(true) - } - ) - const keyboardDidHideListener = Keyboard.addListener( - isIos ? 'keyboardWillHide' : 'keyboardDidHide', - () => { - setKeyboardVisible(false) - } - ) - - return () => { - keyboardDidShowListener.remove() - keyboardDidHideListener.remove() - } - }, [isIos]) - - const maybeTabBarHeight = hasTabs ? MAX_TAB_BAR_HEIGHT : 0 - const maybeNotificationHeight = hasNotifications ? notificationHeight : 0 - - const keyboardAccessoryStyle = React.useMemo( - () => ({ - backgroundColor: 'transparent', - marginBottom: isKeyboardVisible - ? 0 - : safeAreaInsets.bottom + maybeTabBarHeight + maybeNotificationHeight - }), - [ - isKeyboardVisible, - safeAreaInsets.bottom, - maybeTabBarHeight, - maybeNotificationHeight - ] - ) - - return !visible ? null : ( - - - {children} - - - ) -} diff --git a/src/components/scenes/RampCreateScene.tsx b/src/components/scenes/RampCreateScene.tsx index 2919d6aa49a..357c83d156c 100644 --- a/src/components/scenes/RampCreateScene.tsx +++ b/src/components/scenes/RampCreateScene.tsx @@ -57,6 +57,7 @@ import { } from '../../util/utils' import { DropdownInputButton } from '../buttons/DropdownInputButton' import { EdgeButton } from '../buttons/EdgeButton' +import { KavButtons } from '../buttons/KavButtons' import { PillButton } from '../buttons/PillButton' import { AlertCardUi4 } from '../cards/AlertCard' import { ErrorCard, I18nError } from '../cards/ErrorCard' @@ -64,7 +65,6 @@ import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { SceneWrapper } from '../common/SceneWrapper' import { CryptoIcon } from '../icons/CryptoIcon' import { FiatIcon } from '../icons/FiatIcon' -import { KavButton } from '../keyboard/KavButton' import { SceneContainer } from '../layout/SceneContainer' import { FiatListModal } from '../modals/FiatListModal' import { @@ -799,210 +799,210 @@ export const RampCreateScene: React.FC = (props: Props) => { // Render trade form view return ( - <> - - - flagUri != null ? ( - - ) : null - } - label={getRegionText()} - onPress={handleRegionSelect} - /> - } - > - {/* Amount Inputs */} - {/* Top Input (Fiat) */} - - - {selectedFiatFlagUri !== '' ? ( - - - - ) : ( - // Shouldn't be possible to reach this case, but just in case: - // show the fiat currency code as the placeholder - + ) + }} + > + + flagUri != null ? ( + - )} - - - - - - {/* Bottom Input (Crypto by design) */} - - {selectedCryptoCurrencyCode == null && - !isLoadingPersistedCryptoSelection ? ( - - ) : ( - <> - - {isLoadingPersistedCryptoSelection ? ( - - ) : selectedCrypto == null || - selectedWallet == null ? null : ( - - )} - - - + } + > + {/* Amount Inputs */} + {/* Top Input (Fiat) */} + + + {selectedFiatFlagUri !== '' ? ( + + - + + ) : ( + // Shouldn't be possible to reach this case, but just in case: + // show the fiat currency code as the placeholder + )} - - - {/* Wallet Name and MAX Button Row */} - {selectedWallet == null ? null : ( - - {selectedWallet?.name != null ? ( - - {selectedWallet.name} - - ) : null} - - - {lstrings.trade_create_max} - - - - )} - - {/* Exchange Rate */} - {selectedCrypto == null || - selectedWallet == null || - denomination == null || - 'empty' in amountQuery || - lastUsedInput == null || - (!isLoadingQuotes && - !isFetchingQuotes && - allQuotes.length === 0) ? null : ( + + + + + + {/* Bottom Input (Crypto by design) */} + + {selectedCryptoCurrencyCode == null && + !isLoadingPersistedCryptoSelection ? ( + + ) : ( <> - - {lstrings.trade_create_exchange_rate} - - {bestQuote != null ? ( - - {exchangeRateText} - - ) : null} - + {isLoadingPersistedCryptoSelection ? ( + + ) : selectedCrypto == null || selectedWallet == null ? null : ( + + )} + + + )} - - {/* Alert for no supported plugins */} - { - // Nothing is loading - !isResultLoading && - // Nothing was returned - allQuotes.length === 0 && - quoteErrors.length === 0 && - // No other error to show (e.g., insufficient funds) - errorForDisplay == null && - // User has queried - !('empty' in amountQuery) && - lastUsedInput != null && - selectedWallet != null && - selectedCryptoCurrencyCode != null ? ( - - ) : null - } - - {errorForDisplay != null ? ( - - ) : null} - - - {/* Next Button - Must be sibling of SceneWrapper for proper keyboard positioning */} - + + {/* Wallet Name and MAX Button Row */} + {selectedWallet == null ? null : ( + + {selectedWallet?.name != null ? ( + + {selectedWallet.name} + + ) : null} + + + {lstrings.trade_create_max} + + + + )} + + {/* Exchange Rate */} + {selectedCrypto == null || + selectedWallet == null || + denomination == null || + 'empty' in amountQuery || + lastUsedInput == null || + (!isLoadingQuotes && + !isFetchingQuotes && + allQuotes.length === 0) ? null : ( + <> + + {lstrings.trade_create_exchange_rate} + + {bestQuote != null ? ( + + {exchangeRateText} + + ) : null} + + + )} + + {/* Alert for no supported plugins */} + { + // Nothing is loading + !isResultLoading && + // Nothing was returned + allQuotes.length === 0 && + quoteErrors.length === 0 && + // No other error to show (e.g., insufficient funds) + errorForDisplay == null && + // User has queried + !('empty' in amountQuery) && + lastUsedInput != null && + selectedWallet != null && + selectedCryptoCurrencyCode != null ? ( + + ) : null } - /> - + + {errorForDisplay != null ? : null} + + ) } diff --git a/src/components/scenes/RampKycFormScene.tsx b/src/components/scenes/RampKycFormScene.tsx index b4b7ffec87e..d952fb04389 100644 --- a/src/components/scenes/RampKycFormScene.tsx +++ b/src/components/scenes/RampKycFormScene.tsx @@ -6,9 +6,9 @@ import { lstrings } from '../../locales/strings' import { GuiFormField } from '../../plugins/gui/components/GuiFormField' import { GuiFormRow } from '../../plugins/gui/components/GuiFormRow' import type { BuySellTabSceneProps } from '../../types/routerTypes' +import { KavButtons } from '../buttons/KavButtons' import { ErrorCard } from '../cards/ErrorCard' import { SceneWrapper } from '../common/SceneWrapper' -import { KavButton } from '../keyboard/KavButton' import { SceneContainer } from '../layout/SceneContainer' import { showError } from '../services/AirshipInstance' import type { FilledTextInputRef } from '../themed/FilledTextInput' @@ -189,103 +189,111 @@ export const RampKycFormScene = React.memo((props: Props) => { postalCode.trim() !== '' return ( - <> - - - - - - - - - - - - - + ) + }} + > + + - - { - handleSubmit().catch(showError) - }} - returnKeyType="done" - fieldRef={postalCodeRef} + fieldRef={lastNameRef} /> - - {error == null ? null : } - - - - + + + + + + + + + + + + { + handleSubmit().catch(showError) + }} + returnKeyType="done" + fieldRef={postalCodeRef} + /> + + {error == null ? null : } + + ) }) diff --git a/src/plugins/gui/scenes/FiatPluginEnterAmountScene.tsx b/src/plugins/gui/scenes/FiatPluginEnterAmountScene.tsx index e7446ed003c..73342eeb5b2 100644 --- a/src/plugins/gui/scenes/FiatPluginEnterAmountScene.tsx +++ b/src/plugins/gui/scenes/FiatPluginEnterAmountScene.tsx @@ -4,6 +4,7 @@ import { useEffect } from 'react' import { Image, type TextStyle, View } from 'react-native' import { ButtonsView } from '../../../components/buttons/ButtonsView' +import { KavButtons } from '../../../components/buttons/KavButtons' import { PoweredByCard } from '../../../components/cards/PoweredByCard' import { EdgeAnim, @@ -13,7 +14,6 @@ import { fadeInUp60 } from '../../../components/common/EdgeAnim' import { SceneWrapper } from '../../../components/common/SceneWrapper' -import { KavButton } from '../../../components/keyboard/KavButton' import { SceneContainer } from '../../../components/layout/SceneContainer' import { showError } from '../../../components/services/AirshipInstance' import { @@ -236,144 +236,147 @@ export const FiatPluginEnterAmountScene = React.memo((props: Props) => { poweredBy != null ? getPartnerIconUri(poweredBy.poweredByIcon) : undefined return ( - <> - + ) + }} + > + - - - {swapInputLocations ? ( - - - - - - - - {onMax != null ? ( - - - - ) : null} - - ) : ( - - - - - - + {swapInputLocations ? ( + + + + + + + + {onMax != null ? ( + + - - {onMax != null ? ( - - - - ) : null} - - )} - <> - - - {statusText.content} - + + ) : null} + + ) : ( + + + - - + - - - - - - + {onMax != null ? ( + + + + ) : null} + + )} + <> + + + {statusText.content} + + + + + + + + + ) }) diff --git a/yarn.lock b/yarn.lock index 7d946df2c5a..95543d581c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15983,11 +15983,6 @@ react-native-is-edge-to-edge@^1.1.7, react-native-is-edge-to-edge@^1.2.1: resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== -react-native-keyboard-accessory@^0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/react-native-keyboard-accessory/-/react-native-keyboard-accessory-0.1.16.tgz#f0babba9e6568c0c2c8f3fd774fcb2b90b8274ba" - integrity sha512-zpdaduoGjp/spLtM0XxxyxFpCqFQu3fospy/HeYpaIfsXTqGdT1MIajS8tahDxeG6fHLDrWvEIYA6zWM5jni0w== - react-native-keyboard-aware-scroll-view@^0.9.5: version "0.9.5" resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.9.5.tgz#e2e9665d320c188e6b1f22f151b94eb358bf9b71" From 4b97df84874caf47428212076b6839a7bb0fbd9f Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Fri, 7 Nov 2025 16:40:31 -0800 Subject: [PATCH 2/2] Integrate new kav buttons to `SwapCreateScene` --- CHANGELOG.md | 2 + .../SwapCreateScene.test.tsx.snap | 931 +++++++++--------- src/components/scenes/SwapCreateScene.tsx | 187 ++-- src/components/themed/SwapInput.tsx | 11 +- 4 files changed, 582 insertions(+), 549 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e515565c1b..c16d4130ff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - added: Added Infinite ramp plugin. - fixed: Append chain name for L2-native assets in `RampCreateScene` +- changed: `SwapCreateScene` shows a "Cancel" and "Next" button when editing + amounts ## 4.39.0 (staging) diff --git a/src/__tests__/scenes/__snapshots__/SwapCreateScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SwapCreateScene.test.tsx.snap index 04463a97755..b341e21470f 100644 --- a/src/__tests__/scenes/__snapshots__/SwapCreateScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/SwapCreateScene.test.tsx.snap @@ -237,7 +237,7 @@ exports[`SwapCreateScene should render with loading props 1`] = ` "width": 750, }, { - "padding": 11, + "padding": 0, }, ] } @@ -265,572 +265,589 @@ exports[`SwapCreateScene should render with loading props 1`] = ` "maxHeight": 1334, }, { - "padding": 11, + "padding": 0, }, ] } > - + - - - + + - Select Source Wallet - + ] + } + > + Select Source Wallet + + - - - + + - -  - - - +  + + + + /> + - - - + - - - + + - Select Receiving Wallet - + ] + } + > + Select Receiving Wallet + + - - - + + nativeID="5" + /> + , = props => { await showWalletListModal('to') }) - const handleReturnKeyPress = useHandler(() => { + const handleCancelKeyPress = useHandler(() => { Keyboard.dismiss() }) @@ -515,92 +517,109 @@ export const SwapCreateScene: React.FC = props => { hasNotifications scroll keyboardShouldPersistTaps="handled" - padding={theme.rem(0.5)} - > - - {fromWallet == null ? ( - - ) : ( - - )} - - - - - - - {hasMaxSpend ? ( - - - {lstrings.string_max_cap} - - - ) : null} - - - - {toWallet == null ? ( - - ) : ( - - )} - - {renderAlert()} - - {isNextHidden ? null : ( - - )} - + ) + }} + > + {({ isKeyboardOpen }) => ( + + + {fromWallet == null ? ( + + ) : ( + + )} + + + + + + + {hasMaxSpend ? ( + + + {lstrings.string_max_cap} + + + ) : null} + + + + {toWallet == null ? ( + + ) : ( + + )} + + {renderAlert()} + + {isNextHidden || isKeyboardOpen ? null : ( + + )} + + + )} ) } diff --git a/src/components/themed/SwapInput.tsx b/src/components/themed/SwapInput.tsx index dcefd4c4eca..e746b5877ad 100644 --- a/src/components/themed/SwapInput.tsx +++ b/src/components/themed/SwapInput.tsx @@ -1,7 +1,7 @@ import { div, log10, mul, round } from 'biggystring' import type { EdgeCurrencyWallet, EdgeTokenId } from 'edge-core-js' import React, { useMemo } from 'react' -import { type ReturnKeyType, View } from 'react-native' +import { View } from 'react-native' import { useHandler } from '../../hooks/useHandler' import { @@ -52,7 +52,6 @@ export interface Props { forceField?: 'fiat' | 'crypto' keyboardVisible?: boolean placeholders?: [string, string] - returnKeyType?: ReturnKeyType startNativeAmount?: string tokenId: EdgeTokenId wallet: EdgeCurrencyWallet @@ -62,7 +61,6 @@ export interface Props { onBlur?: () => void onFocus?: () => void onNext?: () => void - onReturnKeyPress?: () => void onSelectWallet: () => Promise } @@ -80,7 +78,6 @@ const SwapInputComponent = React.forwardRef( keyboardVisible = true, placeholders, startNativeAmount, - returnKeyType, tokenId, wallet, walletPlaceholderText, @@ -89,8 +86,7 @@ const SwapInputComponent = React.forwardRef( onSelectWallet, onBlur, onFocus, - onNext, - onReturnKeyPress + onNext } = props const exchangeRates = useSelector(state => state.exchangeRates) @@ -353,12 +349,11 @@ const SwapInputComponent = React.forwardRef( ref={flipInputRef} renderFooter={renderFooter} renderHeader={renderHeader} - returnKeyType={returnKeyType} startAmounts={[initialDisplayAmount, initialFiatAmount]} // Events: onBlur={onBlur} onFocus={onFocus} - onNext={onReturnKeyPress ?? onNext} + onNext={onNext} /> )