From 7667a0e9efc761a353c0ea2d9e8b17714995ad13 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 11:33:21 -0400 Subject: [PATCH 01/14] remove boundary for buy list --- .../screens/Swap/components/TokenList/TokenToBuySection.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/__swaps__/screens/Swap/components/TokenList/TokenToBuySection.tsx b/src/__swaps__/screens/Swap/components/TokenList/TokenToBuySection.tsx index dae690ff913..4d697369120 100644 --- a/src/__swaps__/screens/Swap/components/TokenList/TokenToBuySection.tsx +++ b/src/__swaps__/screens/Swap/components/TokenList/TokenToBuySection.tsx @@ -128,9 +128,8 @@ export const TokenToBuySection = ({ section }: { section: AssetToBuySection }) = - {/* TODO: fix this from causing the UI to be completely slow... */} } keyExtractor={item => `${item.uniqueId}-${section.id}`} renderItem={({ item }) => ( From 9d9130a42784a3b455ba3c15c9027590843a2153 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 11:56:06 -0400 Subject: [PATCH 02/14] fix --- src/App.js | 19 +-- src/__swaps__/screens/Swap/Swap.tsx | 48 +++++--- .../components/CleanupAssetsOnUnmount.tsx | 16 +++ .../screens/Swap/components/ReviewPanel.tsx | 6 +- .../Swap/components/SwapInputAsset.tsx | 8 +- .../screens/Swap/components/SwapNumberPad.tsx | 50 ++++---- .../Swap/components/SwapOutputAsset.tsx | 8 +- .../screens/Swap/components/SwapSlider.tsx | 28 ++--- .../screens/Swap/hooks/useCleanupOnExit.ts | 39 +++++++ .../screens/Swap/hooks/usePrefillAssets.ts | 53 +++++++++ .../screens/Swap/hooks/useSwapNavigation.ts | 16 +-- .../screens/Swap/hooks/useSwapTextStyles.ts | 54 ++++----- .../screens/Swap/hooks/useSwapWarning.ts | 8 +- .../screens/Swap/providers/swap-provider.tsx | 110 ++++++++---------- src/__swaps__/types/swap.ts | 14 +++ src/__swaps__/utils/prefillAssets.ts | 43 +++++++ .../ProfileActionButtonsRow.tsx | 2 +- 17 files changed, 346 insertions(+), 176 deletions(-) create mode 100644 src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx create mode 100644 src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts create mode 100644 src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts create mode 100644 src/__swaps__/utils/prefillAssets.ts diff --git a/src/App.js b/src/App.js index 114473f3f37..6ba0ee90353 100644 --- a/src/App.js +++ b/src/App.js @@ -57,6 +57,7 @@ import { handleReviewPromptAction } from '@/utils/reviewAlert'; import { RemotePromoSheetProvider } from '@/components/remote-promo-sheet/RemotePromoSheetProvider'; import { RemoteCardProvider } from '@/components/cards/remote-cards'; import { initializeRemoteConfig } from '@/model/remoteConfig'; +import { SwapProvider } from './__swaps__/screens/Swap/providers/swap-provider'; if (__DEV__) { reactNativeDisableYellowBox && LogBox.ignoreAllLogs(); @@ -223,14 +224,16 @@ class OldApp extends Component { {this.state.initialRoute && ( - - - - - - - - + + + + + + + + + + )} diff --git a/src/__swaps__/screens/Swap/Swap.tsx b/src/__swaps__/screens/Swap/Swap.tsx index 0a42fc22806..0093774c548 100644 --- a/src/__swaps__/screens/Swap/Swap.tsx +++ b/src/__swaps__/screens/Swap/Swap.tsx @@ -20,7 +20,7 @@ import { SliderAndKeyboard } from '@/__swaps__/screens/Swap/components/SliderAnd import { SwapBottomPanel } from '@/__swaps__/screens/Swap/components/SwapBottomPanel'; import { SwapWarning } from './components/SwapWarning'; import { useSwapContext } from './providers/swap-provider'; -import { UserAssetsSync } from './components/UserAssetsSync'; +import { CleanupAssetsOnUnmount } from './components/CleanupAssetsOnUnmount'; /** README * This prototype is largely driven by Reanimated and Gesture Handler, which @@ -60,7 +60,6 @@ import { UserAssetsSync } from './components/UserAssetsSync'; */ export function SwapScreen() { - const { AnimatedSwapStyles } = useSwapContext(); return ( @@ -69,29 +68,42 @@ export function SwapScreen() { - - - - - - - - + + - - {/* NOTE: The components below render null and are solely for keeping react-query and Zustand in sync */} - + + ); } +const SliderAndKeyboardAndBottomControls = () => { + const { AnimatedSwapStyles } = useSwapContext(); + return ( + + + + + ); +}; + +const ExchangeRateBubbleAndWarning = () => { + const { AnimatedSwapStyles } = useSwapContext(); + return ( + + + + + ); +}; + export const styles = StyleSheet.create({ rootViewBackground: { borderRadius: IS_ANDROID ? 20 : ScreenCornerRadius, diff --git a/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx b/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx new file mode 100644 index 00000000000..d77c2ad6129 --- /dev/null +++ b/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx @@ -0,0 +1,16 @@ +import { useCleanupOnExit } from '../hooks/useCleanupOnExit'; +import { useSwapContext } from '../providers/swap-provider'; + +export const CleanupAssetsOnUnmount = () => { + const { SwapInputsController, inputProgress, outputProgress, internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); + + useCleanupOnExit({ + SwapInputsController, + inputProgress, + outputProgress, + internalSelectedInputAsset, + internalSelectedOutputAsset, + }); + + return null; +}; diff --git a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx index 15ca364a16a..83999520648 100644 --- a/src/__swaps__/screens/Swap/components/ReviewPanel.tsx +++ b/src/__swaps__/screens/Swap/components/ReviewPanel.tsx @@ -83,7 +83,7 @@ const RainbowFee = () => { export function ReviewPanel() { const { isDarkMode } = useColorMode(); - const { configProgress, SwapSettings, SwapInputController, internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); + const { configProgress, SwapSettings, SwapInputsController, internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); const unknown = i18n.t(i18n.l.swap.unknown); @@ -92,11 +92,11 @@ export function ReviewPanel() { ); const minimumReceived = useDerivedValue(() => { - if (!SwapInputController.formattedOutputAmount.value || !internalSelectedOutputAsset.value?.symbol) { + if (!SwapInputsController.formattedOutputAmount.value || !internalSelectedOutputAsset.value?.symbol) { return unknown; } - return `${SwapInputController.formattedOutputAmount.value} ${internalSelectedOutputAsset.value.symbol}`; + return `${SwapInputsController.formattedOutputAmount.value} ${internalSelectedOutputAsset.value.symbol}`; }); const handleDecrementSlippage = () => { diff --git a/src/__swaps__/screens/Swap/components/SwapInputAsset.tsx b/src/__swaps__/screens/Swap/components/SwapInputAsset.tsx index 6ca074c82a6..4be63c6b14a 100644 --- a/src/__swaps__/screens/Swap/components/SwapInputAsset.tsx +++ b/src/__swaps__/screens/Swap/components/SwapInputAsset.tsx @@ -42,7 +42,7 @@ function SwapInputActionButton() { } function SwapInputAmount() { - const { focusedInput, SwapTextStyles, SwapInputController, AnimatedSwapStyles } = useSwapContext(); + const { focusedInput, SwapTextStyles, SwapInputsController, AnimatedSwapStyles } = useSwapContext(); return ( @@ -106,7 +106,7 @@ export function SwapInputAsset() { inputProgress, AnimatedSwapStyles, SwapTextStyles, - SwapInputController, + SwapInputsController, internalSelectedInputAsset, SwapNavigation, } = useSwapContext(); @@ -129,7 +129,7 @@ export function SwapInputAsset() { numberOfLines={1} size="17pt" style={SwapTextStyles.inputNativeValueStyle} - text={SwapInputController.formattedInputNativeValue} + text={SwapInputsController.formattedInputNativeValue} weight="heavy" /> diff --git a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx index e20e87dbcf3..b696a8972f2 100644 --- a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx +++ b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx @@ -32,35 +32,35 @@ type numberPadCharacter = number | 'backspace' | '.'; export const SwapNumberPad = () => { const { isDarkMode } = useColorMode(); - const { focusedInput, SwapInputController, configProgress } = useSwapContext(); + const { focusedInput, SwapInputsController, configProgress } = useSwapContext(); const longPressTimer = useSharedValue(0); const addNumber = (number?: number) => { 'worklet'; // immediately stop the quote fetching interval - SwapInputController.quoteFetchingInterval.stop(); + SwapInputsController.quoteFetchingInterval.stop(); const inputKey = focusedInput.value; - if (SwapInputController.inputMethod.value !== inputKey) { - SwapInputController.inputMethod.value = inputKey; + if (SwapInputsController.inputMethod.value !== inputKey) { + SwapInputsController.inputMethod.value = inputKey; - if (typeof SwapInputController.inputValues.value[inputKey] === 'number') { - SwapInputController.inputValues.modify(value => { + if (typeof SwapInputsController.inputValues.value[inputKey] === 'number') { + SwapInputsController.inputValues.modify(value => { return { ...value, [inputKey]: inputKey === 'inputAmount' - ? stripCommas(SwapInputController.formattedInputAmount.value) - : stripCommas(SwapInputController.formattedOutputAmount.value), + ? stripCommas(SwapInputsController.formattedInputAmount.value) + : stripCommas(SwapInputsController.formattedOutputAmount.value), }; }); } } - const currentValue = SwapInputController.inputValues.value[inputKey]; + const currentValue = SwapInputsController.inputValues.value[inputKey]; const newValue = currentValue === 0 || currentValue === '0' ? `${number}` : `${currentValue}${number}`; - SwapInputController.inputValues.modify(value => { + SwapInputsController.inputValues.modify(value => { return { ...value, [inputKey]: newValue, @@ -71,25 +71,25 @@ export const SwapNumberPad = () => { const addDecimalPoint = () => { 'worklet'; const inputKey = focusedInput.value; - const currentValue = SwapInputController.inputValues.value[inputKey].toString(); + const currentValue = SwapInputsController.inputValues.value[inputKey].toString(); if (!currentValue.includes('.')) { - if (SwapInputController.inputMethod.value !== inputKey) { - SwapInputController.inputMethod.value = inputKey; + if (SwapInputsController.inputMethod.value !== inputKey) { + SwapInputsController.inputMethod.value = inputKey; - SwapInputController.inputValues.modify(values => { + SwapInputsController.inputValues.modify(values => { return { ...values, [inputKey]: inputKey === 'inputAmount' - ? stripCommas(SwapInputController.formattedInputAmount.value) - : stripCommas(SwapInputController.formattedOutputAmount.value), + ? stripCommas(SwapInputsController.formattedInputAmount.value) + : stripCommas(SwapInputsController.formattedOutputAmount.value), }; }); } const newValue = `${currentValue}.`; - SwapInputController.inputValues.modify(values => { + SwapInputsController.inputValues.modify(values => { return { ...values, [inputKey]: newValue, @@ -101,24 +101,24 @@ export const SwapNumberPad = () => { const deleteLastCharacter = () => { 'worklet'; const inputKey = focusedInput.value; - if (SwapInputController.inputMethod.value !== inputKey) { - SwapInputController.inputMethod.value = inputKey; + if (SwapInputsController.inputMethod.value !== inputKey) { + SwapInputsController.inputMethod.value = inputKey; - SwapInputController.inputValues.modify(values => { + SwapInputsController.inputValues.modify(values => { return { ...values, [inputKey]: inputKey === 'inputAmount' - ? stripCommas(SwapInputController.formattedInputAmount.value) - : stripCommas(SwapInputController.formattedOutputAmount.value), + ? stripCommas(SwapInputsController.formattedInputAmount.value) + : stripCommas(SwapInputsController.formattedOutputAmount.value), }; }); } - const currentValue = SwapInputController.inputValues.value[inputKey].toString(); + const currentValue = SwapInputsController.inputValues.value[inputKey].toString(); // Handle deletion, ensuring a placeholder zero remains if the entire number is deleted const newValue = currentValue.length > 1 ? currentValue.slice(0, -1) : 0; if (newValue === 0) { - SwapInputController.inputValues.modify(values => { + SwapInputsController.inputValues.modify(values => { return { ...values, inputAmount: 0, @@ -128,7 +128,7 @@ export const SwapNumberPad = () => { }; }); } else { - SwapInputController.inputValues.modify(values => { + SwapInputsController.inputValues.modify(values => { return { ...values, [inputKey]: newValue, diff --git a/src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx b/src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx index 8b3ebf05028..1f39303eb74 100644 --- a/src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx +++ b/src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx @@ -42,7 +42,7 @@ function SwapOutputActionButton() { } function SwapOutputAmount() { - const { focusedInput, SwapTextStyles, SwapInputController, AnimatedSwapStyles } = useSwapContext(); + const { focusedInput, SwapTextStyles, SwapInputsController, AnimatedSwapStyles } = useSwapContext(); return ( @@ -106,7 +106,7 @@ export function SwapOutputAsset() { inputProgress, AnimatedSwapStyles, SwapTextStyles, - SwapInputController, + SwapInputsController, internalSelectedOutputAsset, SwapNavigation, } = useSwapContext(); @@ -129,7 +129,7 @@ export function SwapOutputAsset() { numberOfLines={1} size="17pt" style={SwapTextStyles.outputNativeValueStyle} - text={SwapInputController.formattedOutputNativeValue} + text={SwapInputsController.formattedOutputNativeValue} weight="heavy" /> diff --git a/src/__swaps__/screens/Swap/components/SwapSlider.tsx b/src/__swaps__/screens/Swap/components/SwapSlider.tsx index 87fca3e4789..fb97c6b8193 100644 --- a/src/__swaps__/screens/Swap/components/SwapSlider.tsx +++ b/src/__swaps__/screens/Swap/components/SwapSlider.tsx @@ -56,7 +56,7 @@ export const SwapSlider = ({ const { isDarkMode } = useColorMode(); const { AnimatedSwapStyles, - SwapInputController, + SwapInputsController, internalSelectedInputAsset, internalSelectedOutputAsset, sliderXPosition, @@ -77,9 +77,9 @@ export const SwapSlider = ({ // Callback function to handle percentage change once slider is at rest const onChangeWrapper = useCallback( (percentage: number, setStale = true) => { - SwapInputController.onChangedPercentage(percentage, setStale); + SwapInputsController.onChangedPercentage(percentage, setStale); }, - [SwapInputController] + [SwapInputsController] ); const colors = useDerivedValue(() => ({ @@ -115,7 +115,7 @@ export const SwapSlider = ({ useAnimatedReaction( () => ({ x: sliderXPosition.value }), (current, previous) => { - if (current !== previous && SwapInputController.inputMethod.value === 'slider') { + if (current !== previous && SwapInputsController.inputMethod.value === 'slider') { if (current.x >= width * 0.995 && previous?.x && previous?.x < width * 0.995) { runOnJS(triggerHapticFeedback)('impactMedium'); } @@ -130,7 +130,7 @@ export const SwapSlider = ({ const onPressDown = useAnimatedGestureHandler({ onStart: () => { sliderPressProgress.value = withSpring(1, sliderConfig); - SwapInputController.quoteFetchingInterval.stop(); + SwapInputsController.quoteFetchingInterval.stop(); }, onActive: () => { sliderPressProgress.value = withSpring(SLIDER_COLLAPSED_HEIGHT / height, sliderConfig); @@ -141,9 +141,9 @@ export const SwapSlider = ({ onStart: (_, ctx: { startX: number }) => { ctx.startX = sliderXPosition.value; sliderPressProgress.value = withSpring(1, sliderConfig); - SwapInputController.inputMethod.value = 'slider'; + SwapInputsController.inputMethod.value = 'slider'; - // On Android, for some reason waiting until onActive to set SwapInputController.isQuoteStale.value = 1 + // On Android, for some reason waiting until onActive to set SwapInputsController.isQuoteStale.value = 1 // causes the outputAmount text color to break. It's preferable to set it in // onActive, so we're setting it in onStart for Android only. It's possible that // migrating this handler to the RNGH v2 API will remove the need for this. @@ -193,7 +193,7 @@ export const SwapSlider = ({ } else if (xPercentage.value < 0.005) { runOnJS(onChangeWrapper)(0); sliderXPosition.value = withSpring(0, snappySpringConfig); - // SwapInputController.isQuoteStale.value = 0; + // SwapInputsController.isQuoteStale.value = 0; } else { runOnJS(onChangeWrapper)(xPercentage.value); } @@ -245,7 +245,7 @@ export const SwapSlider = ({ sliderXPosition.value = withSpring(nextSnapPoint, snappierSpringConfig); // if (nextSnapPoint === 0) { - // SwapInputController.isQuoteStale.value = 0; + // SwapInputsController.isQuoteStale.value = 0; // } } else { // For low-velocity drags, skip snap points and let the slider rest at current position @@ -331,9 +331,9 @@ export const SwapSlider = ({ const percentageTextStyle = useAnimatedStyle(() => { const isAdjustingInputValue = - SwapInputController.inputMethod.value === 'inputAmount' || SwapInputController.inputMethod.value === 'inputNativeValue'; + SwapInputsController.inputMethod.value === 'inputAmount' || SwapInputsController.inputMethod.value === 'inputNativeValue'; const isAdjustingOutputValue = - SwapInputController.inputMethod.value === 'outputAmount' || SwapInputController.inputMethod.value === 'outputNativeValue'; + SwapInputsController.inputMethod.value === 'outputAmount' || SwapInputsController.inputMethod.value === 'outputNativeValue'; const isStale = isQuoteStale.value === 1 && (isAdjustingInputValue || isAdjustingOutputValue) ? 1 : 0; @@ -345,7 +345,7 @@ export const SwapSlider = ({ isStale, [0, 1], [ - (SwapInputController.inputMethod.value === 'slider' ? xPercentage.value < 0.005 : sliderXPosition.value === 0) + (SwapInputsController.inputMethod.value === 'slider' ? xPercentage.value < 0.005 : sliderXPosition.value === 0) ? zeroAmountColor : labelSecondary, zeroAmountColor, @@ -394,8 +394,8 @@ export const SwapSlider = ({ onPress={() => { 'worklet'; - SwapInputController.quoteFetchingInterval.stop(); - SwapInputController.inputMethod.value = 'slider'; + SwapInputsController.quoteFetchingInterval.stop(); + SwapInputsController.inputMethod.value = 'slider'; setTimeout(() => { sliderXPosition.value = withSpring(width, snappySpringConfig); onChangeWrapper(1); diff --git a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts new file mode 100644 index 00000000000..8616b83d39e --- /dev/null +++ b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts @@ -0,0 +1,39 @@ +import { useEffect } from 'react'; +import { useSwapInputsController } from './useSwapInputsController'; +import { swapsStore } from '@/state/swaps/swapsStore'; +import { userAssetsStore } from '@/state/assets/userAssets'; +import { parseSearchAsset } from '@/__swaps__/utils/assets'; +import { SharedValue } from 'react-native-reanimated'; +import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; +import { parseAssetAndExtend } from '@/__swaps__/utils/swaps'; +import { NavigationSteps } from './useSwapNavigation'; +import { useSwapContext } from '../providers/swap-provider'; +import { SwapAssetType } from '@/__swaps__/types/swap'; + +export const useCleanupOnExit = () => { + const { setAsset, SwapInputsController } = useSwapContext(); + + useEffect(() => { + return () => { + try { + // reset back to the user's asset with largest balance + const firstUserAsset = userAssetsStore.getState().userAssets.values().next().value; + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: firstUserAsset, + userAsset: firstUserAsset, + }); + + setAsset({ + type: SwapAssetType.inputAsset, + asset: parsedAsset, + }); + + SwapInputsController.quoteFetchingInterval.stop(); + } catch (err) { + console.log(err); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); +}; diff --git a/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts b/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts new file mode 100644 index 00000000000..9fcfaad68de --- /dev/null +++ b/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts @@ -0,0 +1,53 @@ +import { useEffect } from 'react'; + +import { RootStackParamList } from '@/navigation/types'; +import { RouteProp, useRoute } from '@react-navigation/native'; +import { SwapAssetType } from '@/__swaps__/types/swap'; +import { ParsedSearchAsset } from '@/__swaps__/types/assets'; +import { userAssetsStore } from '@/state/assets/userAssets'; +import { parseSearchAsset } from '@/__swaps__/utils/assets'; +import { SearchAsset } from '@/__swaps__/types/search'; + +export const usePrefillAssets = ({ + setAsset, +}: { + setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset }) => void; +}) => { + const { params = {} } = useRoute>(); + + const { inputAssetUniqueId, outputAssetUniqueId } = params; + + useEffect(() => { + if (inputAssetUniqueId) { + // TODO: do we need to handle the case where the input asset isn't defined? + const userAsset = userAssetsStore.getState().getUserAsset(inputAssetUniqueId); + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: userAsset as SearchAsset, + userAsset, + }); + + setAsset({ + type: SwapAssetType.inputAsset, + asset: parsedAsset, + }); + } + + if (outputAssetUniqueId) { + // TODO: do we need to handle the case where the output asset isn't defined? + const userAsset = userAssetsStore.getState().getUserAsset(outputAssetUniqueId); + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: userAsset as SearchAsset, + userAsset, + }); + + setAsset({ + type: SwapAssetType.outputAsset, + asset: parsedAsset, + }); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); +}; diff --git a/src/__swaps__/screens/Swap/hooks/useSwapNavigation.ts b/src/__swaps__/screens/Swap/hooks/useSwapNavigation.ts index 13ae083d95b..5672127624f 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapNavigation.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapNavigation.ts @@ -11,12 +11,12 @@ export const enum NavigationSteps { } export function useSwapNavigation({ - SwapInputController, + SwapInputsController, inputProgress, outputProgress, configProgress, }: { - SwapInputController: ReturnType; + SwapInputsController: ReturnType; inputProgress: SharedValue; outputProgress: SharedValue; configProgress: SharedValue; @@ -66,7 +66,7 @@ export function useSwapNavigation({ 'worklet'; handleDismissReview(); handleDismissGas(); - SwapInputController.fetchQuoteAndAssetPrices(); + SwapInputsController.fetchQuoteAndAssetPrices(); if (inputProgress.value === NavigationSteps.TOKEN_LIST_FOCUSED) { inputProgress.value = NavigationSteps.INPUT_ELEMENT_FOCUSED; @@ -80,7 +80,7 @@ export function useSwapNavigation({ if (outputProgress.value === NavigationSteps.SEARCH_FOCUSED) { outputProgress.value = NavigationSteps.TOKEN_LIST_FOCUSED; } - }, [SwapInputController, handleDismissGas, handleDismissReview, inputProgress, outputProgress]); + }, [SwapInputsController, handleDismissGas, handleDismissReview, inputProgress, outputProgress]); const handleFocusInputSearch = useCallback(() => { 'worklet'; @@ -106,7 +106,7 @@ export function useSwapNavigation({ 'worklet'; handleDismissReview(); handleDismissGas(); - SwapInputController.quoteFetchingInterval.stop(); + SwapInputsController.quoteFetchingInterval.stop(); if (inputProgress.value === NavigationSteps.INPUT_ELEMENT_FOCUSED) { console.log('showing token list'); @@ -115,13 +115,13 @@ export function useSwapNavigation({ } else { inputProgress.value = NavigationSteps.INPUT_ELEMENT_FOCUSED; } - }, [handleDismissReview, handleDismissGas, inputProgress, outputProgress, SwapInputController]); + }, [handleDismissReview, handleDismissGas, inputProgress, outputProgress, SwapInputsController]); const handleOutputPress = useCallback(() => { 'worklet'; handleDismissReview(); handleDismissGas(); - SwapInputController.quoteFetchingInterval.stop(); + SwapInputsController.quoteFetchingInterval.stop(); if (outputProgress.value === NavigationSteps.INPUT_ELEMENT_FOCUSED) { outputProgress.value = NavigationSteps.TOKEN_LIST_FOCUSED; @@ -129,7 +129,7 @@ export function useSwapNavigation({ } else { outputProgress.value = NavigationSteps.INPUT_ELEMENT_FOCUSED; } - }, [SwapInputController, handleDismissReview, handleDismissGas, inputProgress, outputProgress]); + }, [SwapInputsController, handleDismissReview, handleDismissGas, inputProgress, outputProgress]); const handleSwapAction = useCallback(() => { 'worklet'; diff --git a/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts b/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts index 4b65d56c23f..ffe39945345 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapTextStyles.ts @@ -27,7 +27,7 @@ import { useSwapInputsController } from './useSwapInputsController'; import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; export function useSwapTextStyles({ - SwapInputController, + SwapInputsController, internalSelectedInputAsset, internalSelectedOutputAsset, isQuoteStale, @@ -36,7 +36,7 @@ export function useSwapTextStyles({ outputProgress, sliderPressProgress, }: { - SwapInputController: ReturnType; + SwapInputsController: ReturnType; internalSelectedInputAsset: SharedValue; internalSelectedOutputAsset: SharedValue; isQuoteStale: SharedValue; @@ -53,15 +53,15 @@ export function useSwapTextStyles({ const isInputStale = useDerivedValue(() => { const isAdjustingOutputValue = - SwapInputController.inputMethod.value === 'outputAmount' || SwapInputController.inputMethod.value === 'outputNativeValue'; + SwapInputsController.inputMethod.value === 'outputAmount' || SwapInputsController.inputMethod.value === 'outputNativeValue'; return isQuoteStale.value === 1 && isAdjustingOutputValue ? 1 : 0; }); const isOutputStale = useDerivedValue(() => { const isAdjustingInputValue = - SwapInputController.inputMethod.value === 'inputAmount' || - SwapInputController.inputMethod.value === 'inputNativeValue' || - SwapInputController.inputMethod.value === 'slider'; + SwapInputsController.inputMethod.value === 'inputAmount' || + SwapInputsController.inputMethod.value === 'inputNativeValue' || + SwapInputsController.inputMethod.value === 'slider'; return isQuoteStale.value === 1 && isAdjustingInputValue ? 1 : 0; }); @@ -73,9 +73,9 @@ export function useSwapTextStyles({ const inputAmountTextStyle = useAnimatedStyle(() => { const isInputZero = - (SwapInputController.inputValues.value.inputAmount === 0 && SwapInputController.inputMethod.value !== 'slider') || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + (SwapInputsController.inputValues.value.inputAmount === 0 && SwapInputsController.inputMethod.value !== 'slider') || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; // eslint-disable-next-line no-nested-ternary const zeroOrAssetColor = isInputZero @@ -95,9 +95,9 @@ export function useSwapTextStyles({ const inputNativeValueStyle = useAnimatedStyle(() => { const isInputZero = - Number(SwapInputController.inputValues.value.inputAmount) === 0 || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + Number(SwapInputsController.inputValues.value.inputAmount) === 0 || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; const zeroOrColor = isInputZero ? zeroAmountColor : labelTertiary; const opacity = isInputStale.value !== 1 || (isInputZero && isOutputZero) ? withSpring(1, sliderConfig) : pulsingOpacity.value; @@ -110,11 +110,11 @@ export function useSwapTextStyles({ const outputAmountTextStyle = useAnimatedStyle(() => { const isInputZero = - Number(SwapInputController.inputValues.value.inputAmount) === 0 || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); + Number(SwapInputsController.inputValues.value.inputAmount) === 0 || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); const isOutputZero = - (SwapInputController.inputValues.value.outputAmount === 0 && SwapInputController.inputMethod.value !== 'slider') || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.outputAmount) === 0); + (SwapInputsController.inputValues.value.outputAmount === 0 && SwapInputsController.inputMethod.value !== 'slider') || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.outputAmount) === 0); // eslint-disable-next-line no-nested-ternary const zeroOrAssetColor = isOutputZero @@ -134,9 +134,9 @@ export function useSwapTextStyles({ const outputNativeValueStyle = useAnimatedStyle(() => { const isInputZero = - Number(SwapInputController.inputValues.value.inputAmount) === 0 || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + Number(SwapInputsController.inputValues.value.inputAmount) === 0 || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; const zeroOrColor = isOutputZero ? zeroAmountColor : labelTertiary; const opacity = isOutputStale.value !== 1 || (isInputZero && isOutputZero) ? withSpring(1, sliderConfig) : pulsingOpacity.value; @@ -153,8 +153,8 @@ export function useSwapTextStyles({ focusedInput.value === 'inputAmount' && inputProgress.value === 0 && outputProgress.value === 0 && - (SwapInputController.inputMethod.value !== 'slider' || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0) || + (SwapInputsController.inputMethod.value !== 'slider' || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0) || (sliderPressProgress.value === SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT && isQuoteStale.value === 0)); const opacity = shouldShow @@ -171,8 +171,8 @@ export function useSwapTextStyles({ : withTiming(0, caretConfig); const isZero = - (SwapInputController.inputMethod.value !== 'slider' && SwapInputController.inputValues.value.inputAmount === 0) || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); + (SwapInputsController.inputMethod.value !== 'slider' && SwapInputsController.inputValues.value.inputAmount === 0) || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); return { display: shouldShow ? 'flex' : 'none', @@ -186,8 +186,8 @@ export function useSwapTextStyles({ focusedInput.value === 'outputAmount' && inputProgress.value === 0 && outputProgress.value === 0 && - (SwapInputController.inputMethod.value !== 'slider' || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0) || + (SwapInputsController.inputMethod.value !== 'slider' || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0) || (sliderPressProgress.value === SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT && isQuoteStale.value === 0)); const opacity = shouldShow @@ -204,8 +204,8 @@ export function useSwapTextStyles({ : withTiming(0, caretConfig); const isZero = - (SwapInputController.inputMethod.value !== 'slider' && SwapInputController.inputValues.value.outputAmount === 0) || - (SwapInputController.inputMethod.value === 'slider' && Number(SwapInputController.inputValues.value.inputAmount) === 0); + (SwapInputsController.inputMethod.value !== 'slider' && SwapInputsController.inputValues.value.outputAmount === 0) || + (SwapInputsController.inputMethod.value === 'slider' && Number(SwapInputsController.inputValues.value.inputAmount) === 0); return { display: shouldShow ? 'flex' : 'none', diff --git a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts index 5c62bad1f63..014b22d4a56 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts @@ -40,7 +40,7 @@ export interface SwapTimeEstimate { } type UsePriceImpactWarningProps = { - SwapInputController: ReturnType; + SwapInputsController: ReturnType; inputAsset: SharedValue; outputAsset: SharedValue; quote: SharedValue; @@ -58,7 +58,7 @@ type CurrentProps = { }; export const useSwapWarning = ({ - SwapInputController, + SwapInputsController, inputAsset, outputAsset, quote, @@ -169,8 +169,8 @@ export const useSwapWarning = ({ () => ({ inputAsset: inputAsset.value, outputAsset: outputAsset.value, - inputNativeValue: SwapInputController.inputValues.value.inputNativeValue, - outputNativeValue: SwapInputController.inputValues.value.outputNativeValue, + inputNativeValue: SwapInputsController.inputValues.value.inputNativeValue, + outputNativeValue: SwapInputsController.inputValues.value.outputNativeValue, quote: quote.value, isFetching: isFetching.value, isQuoteStale: isQuoteStale.value, diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index 3b37c628832..53445323b0d 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -1,5 +1,4 @@ -// @refresh -import React, { createContext, useContext, ReactNode, useEffect } from 'react'; +import React, { createContext, useContext, ReactNode } from 'react'; import { StyleProp, TextStyle, TextInput } from 'react-native'; import { AnimatedRef, @@ -22,10 +21,11 @@ import { useSwapGas } from '@/__swaps__/screens/Swap/hooks/useSwapGas'; import { useSwapSettings } from '@/__swaps__/screens/Swap/hooks/useSwapSettings'; import { CrosschainQuote, Quote, QuoteError } from '@rainbow-me/swaps'; import { swapsStore } from '@/state/swaps/swapsStore'; -import { isSameAsset } from '@/__swaps__/utils/assets'; +import { isSameAsset, parseSearchAsset } from '@/__swaps__/utils/assets'; import { parseAssetAndExtend } from '@/__swaps__/utils/swaps'; import { ChainId } from '@/__swaps__/types/chains'; import { logger } from '@/logger'; +import { userAssetsStore } from '@/state/assets/userAssets'; interface SwapContextType { isFetching: SharedValue; @@ -53,7 +53,7 @@ interface SwapContextType { quote: SharedValue; SwapSettings: ReturnType; - SwapInputController: ReturnType; + SwapInputsController: ReturnType; AnimatedSwapStyles: ReturnType; SwapTextStyles: ReturnType; SwapNavigation: ReturnType; @@ -78,7 +78,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { const searchInputRef = useAnimatedRef(); const inputProgress = useSharedValue(NavigationSteps.INPUT_ELEMENT_FOCUSED); - const outputProgress = useSharedValue(NavigationSteps.INPUT_ELEMENT_FOCUSED); + const outputProgress = useSharedValue(NavigationSteps.TOKEN_LIST_FOCUSED); const configProgress = useSharedValue(NavigationSteps.INPUT_ELEMENT_FOCUSED); const sliderXPosition = useSharedValue(SLIDER_WIDTH * INITIAL_SLIDER_POSITION); @@ -87,10 +87,23 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { const lastTypedInput = useSharedValue('inputAmount'); const focusedInput = useSharedValue('inputAmount'); - const selectedOutputChainId = useSharedValue(ChainId.mainnet); + // NOTE: On mount let's set the initial input asset to the first user asset (aka the user asset with the largest balance) + const INTERNAL_INITIAL_USER_ASSET = userAssetsStore.getState().userAssets.values().next().value; - const internalSelectedInputAsset = useSharedValue(null); + const internalSelectedInputAsset = useSharedValue( + parseAssetAndExtend({ asset: INTERNAL_INITIAL_USER_ASSET }) + ); const internalSelectedOutputAsset = useSharedValue(null); + const selectedOutputChainId = useSharedValue(INTERNAL_INITIAL_USER_ASSET?.chainId ?? ChainId.mainnet); + + if (INTERNAL_INITIAL_USER_ASSET) { + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: INTERNAL_INITIAL_USER_ASSET, + userAsset: INTERNAL_INITIAL_USER_ASSET, + }); + swapsStore.setState({ inputAsset: parsedAsset }); + } const quote = useSharedValue(null); @@ -103,7 +116,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { outputAsset: internalSelectedOutputAsset, }); - const SwapInputController = useSwapInputsController({ + const SwapInputsController = useSwapInputsController({ focusedInput, lastTypedInput, inputProgress, @@ -117,14 +130,14 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }); const SwapNavigation = useSwapNavigation({ - SwapInputController, + SwapInputsController, inputProgress, outputProgress, configProgress, }); const SwapWarning = useSwapWarning({ - SwapInputController, + SwapInputsController, inputAsset: internalSelectedInputAsset, outputAsset: internalSelectedOutputAsset, quote, @@ -144,7 +157,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }); const SwapTextStyles = useSwapTextStyles({ - SwapInputController, + SwapInputsController, internalSelectedInputAsset, internalSelectedOutputAsset, isQuoteStale, @@ -185,13 +198,8 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }; const setSelectedOutputChainId = (chainId: ChainId) => { - const updateChainId = (chainId: ChainId) => { - 'worklet'; - selectedOutputChainId.value = chainId; - }; - swapsStore.setState({ selectedOutputChainId: chainId }); - runOnUI(updateChainId)(chainId); + selectedOutputChainId.value = chainId; }; const setAsset = ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset }) => { @@ -201,7 +209,6 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { switch (type) { case SwapAssetType.inputAsset: internalSelectedInputAsset.value = asset; - selectedOutputChainId.value = asset?.chainId ?? ChainId.mainnet; break; case SwapAssetType.outputAsset: internalSelectedOutputAsset.value = asset; @@ -213,28 +220,20 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }); }; - // const prevAsset = swapsStore.getState()[type]; - const prevOtherAsset = swapsStore.getState()[type === SwapAssetType.inputAsset ? SwapAssetType.outputAsset : SwapAssetType.inputAsset]; - - // TODO: Fix me. This is causing assets to not be set sometimes? - // if we're setting the same asset, exit early as it's a no-op - // if (prevAsset && isSameAsset(prevAsset, asset)) { - // logger.debug(`[setAsset]: Not setting ${type} asset as it's the same as what is already set`); - // handleProgressNavigation({ - // type, - // inputAsset: type === SwapAssetType.inputAsset ? asset : prevOtherAsset, - // outputAsset: type === SwapAssetType.outputAsset ? asset : prevOtherAsset, - // }); - // return; - // } + const otherAsset = swapsStore.getState()[type === SwapAssetType.inputAsset ? SwapAssetType.outputAsset : SwapAssetType.inputAsset]; // if we're setting the same asset as the other asset, we need to clear the other asset - if (prevOtherAsset && isSameAsset(prevOtherAsset, asset)) { + if (otherAsset && isSameAsset(otherAsset, asset)) { logger.debug(`[setAsset]: Swapping ${type} asset for ${type === SwapAssetType.inputAsset ? 'output' : 'input'} asset`); swapsStore.setState({ [type === SwapAssetType.inputAsset ? SwapAssetType.outputAsset : SwapAssetType.inputAsset]: null, }); + + if (type === SwapAssetType.inputAsset) { + setSelectedOutputChainId(otherAsset.chainId); + } + runOnUI(updateAssetValue)({ type: type === SwapAssetType.inputAsset ? SwapAssetType.outputAsset : SwapAssetType.inputAsset, asset: null, @@ -246,6 +245,9 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { swapsStore.setState({ [type]: asset, }); + if (type === SwapAssetType.inputAsset) { + setSelectedOutputChainId(asset.chainId); + } runOnUI(updateAssetValue)({ type, asset: parseAssetAndExtend({ asset }) }); }; @@ -260,12 +262,12 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { return ''; } - const isInputZero = Number(SwapInputController.inputValues.value.inputAmount) === 0; - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + const isInputZero = Number(SwapInputsController.inputValues.value.inputAmount) === 0; + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; - if (SwapInputController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value) { + if (SwapInputsController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value) { return ''; - } else if (SwapInputController.inputMethod.value === 'slider' && SwapInputController.percentageToSwap.value === 0) { + } else if (SwapInputsController.inputMethod.value === 'slider' && SwapInputsController.percentageToSwap.value === 0) { return ''; } else { return '􀕹'; @@ -283,14 +285,14 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { return 'Fetching prices'; } - const isInputZero = Number(SwapInputController.inputValues.value.inputAmount) === 0; - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + const isInputZero = Number(SwapInputsController.inputValues.value.inputAmount) === 0; + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; - if (SwapInputController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value) { + if (SwapInputsController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value) { return 'Enter Amount'; } else if ( - SwapInputController.inputMethod.value === 'slider' && - (SwapInputController.percentageToSwap.value === 0 || isInputZero || isOutputZero) + SwapInputsController.inputMethod.value === 'slider' && + (SwapInputsController.percentageToSwap.value === 0 || isInputZero || isOutputZero) ) { return 'Enter Amount'; } else { @@ -299,13 +301,13 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }); const confirmButtonIconStyle = useAnimatedStyle(() => { - const isInputZero = Number(SwapInputController.inputValues.value.inputAmount) === 0; - const isOutputZero = Number(SwapInputController.inputValues.value.outputAmount) === 0; + const isInputZero = Number(SwapInputsController.inputValues.value.inputAmount) === 0; + const isOutputZero = Number(SwapInputsController.inputValues.value.outputAmount) === 0; const sliderCondition = - SwapInputController.inputMethod.value === 'slider' && - (SwapInputController.percentageToSwap.value === 0 || isInputZero || isOutputZero); - const inputCondition = SwapInputController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value; + SwapInputsController.inputMethod.value === 'slider' && + (SwapInputsController.percentageToSwap.value === 0 || isInputZero || isOutputZero); + const inputCondition = SwapInputsController.inputMethod.value !== 'slider' && (isInputZero || isOutputZero) && !isFetching.value; const shouldHide = sliderCondition || inputCondition; @@ -314,18 +316,6 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }; }); - useEffect(() => { - return () => { - swapsStore.setState({ - inputAsset: null, - outputAsset: null, - quote: null, - }); - - SwapInputController.quoteFetchingInterval.stop(); - }; - }, []); - console.log('re-rendered swap provider: ', Date.now()); return ( @@ -355,7 +345,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { quote, SwapSettings, - SwapInputController, + SwapInputsController, AnimatedSwapStyles, SwapTextStyles, SwapNavigation, diff --git a/src/__swaps__/types/swap.ts b/src/__swaps__/types/swap.ts index 018f0c5f2e2..86ed118e6d1 100644 --- a/src/__swaps__/types/swap.ts +++ b/src/__swaps__/types/swap.ts @@ -1,3 +1,5 @@ +import { UniqueId } from './assets'; + export type inputKeys = 'inputAmount' | 'inputNativeValue' | 'outputAmount' | 'outputNativeValue'; export type settingsKeys = 'swapFee' | 'slippage' | 'flashbots'; export type inputMethods = inputKeys | 'slider'; @@ -10,3 +12,15 @@ export enum SwapAssetType { inputAsset = 'inputAsset', outputAsset = 'outputAsset', } + +export type PrefillAssetData = { + // assets + inputAssetUniqueId?: UniqueId; + outputAssetUniqueId?: UniqueId; + + // amounts + percentageOfInputAssetToSell?: number; + percentageOfOutputAssetToBuy?: number; + amountOfInputAssetToSell?: number; + amountOfOutputAssetToBuy?: number; +}; diff --git a/src/__swaps__/utils/prefillAssets.ts b/src/__swaps__/utils/prefillAssets.ts new file mode 100644 index 00000000000..b6fb84c0294 --- /dev/null +++ b/src/__swaps__/utils/prefillAssets.ts @@ -0,0 +1,43 @@ +import { userAssetsStore } from '@/state/assets/userAssets'; +import { UniqueId } from '../types/assets'; +import { useSwapContext } from '../screens/Swap/providers/swap-provider'; + +export enum EntryPoint { + ProfileActionButtonsRow = 'ProfileActionButtonsRow', + AssetChart = 'AssetChart', +} + +export type EntryPointData = { + inputAssetUniqueId?: UniqueId; + outputAssetUniqueId?: UniqueId; + + // amounts + percentageOfInputAssetToSell?: number; + percentageOfOutputAssetToBuy?: number; + amountOfInputAssetToSell?: number; + amountOfOutputAssetToBuy?: number; +}; + +export const usePrefillAssets = (entryPoint: EntryPoint, data?: EntryPointData) => { + const { internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); + + // NOTE: User assets are sorted by balance in descending order. + const userAssets = userAssetsStore.getState().userAssets; + + switch (entryPoint) { + /** + * If we're coming to the swap screen from the profile action buttons row, we need to prefill the inputAsset + * to the user asset with the largest balance. We should also open the output token list so that they can + * select an output asset. + */ + default: + case EntryPoint.ProfileActionButtonsRow: + break; + + /** + * If we're coming to the swap screen from an asset chart, we need to + */ + case EntryPoint.AssetChart: + break; + } +}; diff --git a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx index 80694a8bdbb..bcc32b81ec5 100644 --- a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx +++ b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx @@ -180,7 +180,7 @@ function SwapButton() { android && delayNext(); if (swapsV2Enabled) { - navigate(Routes.SWAP_NAVIGATOR); + navigate(Routes.SWAP); return; } From ebf46e352c8216ef5f35bfc5654fe5a878d88059 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 13:54:26 -0400 Subject: [PATCH 03/14] fix output asset not being reset --- .../screens/Swap/hooks/useCleanupOnExit.ts | 40 +++++++++---------- .../screens/Swap/providers/swap-provider.tsx | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts index 8616b83d39e..0417bdd3285 100644 --- a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts +++ b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts @@ -1,38 +1,34 @@ import { useEffect } from 'react'; -import { useSwapInputsController } from './useSwapInputsController'; import { swapsStore } from '@/state/swaps/swapsStore'; import { userAssetsStore } from '@/state/assets/userAssets'; import { parseSearchAsset } from '@/__swaps__/utils/assets'; -import { SharedValue } from 'react-native-reanimated'; -import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets'; -import { parseAssetAndExtend } from '@/__swaps__/utils/swaps'; -import { NavigationSteps } from './useSwapNavigation'; import { useSwapContext } from '../providers/swap-provider'; import { SwapAssetType } from '@/__swaps__/types/swap'; export const useCleanupOnExit = () => { - const { setAsset, SwapInputsController } = useSwapContext(); + const { setAsset, internalSelectedOutputAsset, SwapInputsController } = useSwapContext(); useEffect(() => { return () => { - try { - // reset back to the user's asset with largest balance - const firstUserAsset = userAssetsStore.getState().userAssets.values().next().value; - const parsedAsset = parseSearchAsset({ - assetWithPrice: undefined, - searchAsset: firstUserAsset, - userAsset: firstUserAsset, - }); + const firstUserAsset = userAssetsStore.getState().userAssets.values().next().value; + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: firstUserAsset, + userAsset: firstUserAsset, + }); - setAsset({ - type: SwapAssetType.inputAsset, - asset: parsedAsset, - }); + // reset back to the user's asset with largest balance + setAsset({ + type: SwapAssetType.inputAsset, + asset: parsedAsset, + }); - SwapInputsController.quoteFetchingInterval.stop(); - } catch (err) { - console.log(err); - } + // reset output asset to null + internalSelectedOutputAsset.value = null; + swapsStore.setState({ outputAsset: null }); + + // stop quote fetching interval if it was set + SwapInputsController.quoteFetchingInterval.stop(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index 53445323b0d..2fb909e1a30 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -48,7 +48,7 @@ interface SwapContextType { internalSelectedInputAsset: SharedValue; internalSelectedOutputAsset: SharedValue; - setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset }) => void; + setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset | null }) => void; quote: SharedValue; From ab65f48653e30f2f8cb17345c1ac7801f9b29ad3 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 13:56:26 -0400 Subject: [PATCH 04/14] cleanup --- src/__swaps__/screens/Swap/providers/swap-provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index 2fb909e1a30..53445323b0d 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -48,7 +48,7 @@ interface SwapContextType { internalSelectedInputAsset: SharedValue; internalSelectedOutputAsset: SharedValue; - setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset | null }) => void; + setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset }) => void; quote: SharedValue; From d697bd58af88f5fa220a56517bda3baa87120011 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 15:16:37 -0400 Subject: [PATCH 05/14] add more resets --- .../screens/Swap/hooks/useCleanupOnExit.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts index 0417bdd3285..785de80d78a 100644 --- a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts +++ b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts @@ -4,9 +4,21 @@ import { userAssetsStore } from '@/state/assets/userAssets'; import { parseSearchAsset } from '@/__swaps__/utils/assets'; import { useSwapContext } from '../providers/swap-provider'; import { SwapAssetType } from '@/__swaps__/types/swap'; +import { INITIAL_SLIDER_POSITION, SLIDER_COLLAPSED_HEIGHT, SLIDER_HEIGHT, SLIDER_WIDTH } from '../constants'; export const useCleanupOnExit = () => { - const { setAsset, internalSelectedOutputAsset, SwapInputsController } = useSwapContext(); + const { + setAsset, + quote, + isFetching, + isQuoteStale, + sliderXPosition, + sliderPressProgress, + lastTypedInput, + focusedInput, + internalSelectedOutputAsset, + SwapInputsController, + } = useSwapContext(); useEffect(() => { return () => { @@ -27,8 +39,18 @@ export const useCleanupOnExit = () => { internalSelectedOutputAsset.value = null; swapsStore.setState({ outputAsset: null }); + // reset input states + lastTypedInput.value = 'inputAmount'; + focusedInput.value = 'inputAmount'; + // stop quote fetching interval if it was set SwapInputsController.quoteFetchingInterval.stop(); + quote.value = null; + isFetching.value = false; + isQuoteStale.value = 0; + + sliderXPosition.value = SLIDER_WIDTH * INITIAL_SLIDER_POSITION; + sliderPressProgress.value = SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); From 7e740240501d433156a900643684d5bddde01960 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 15:25:58 -0400 Subject: [PATCH 06/14] fix warning being leftover from previously opened screen --- src/__swaps__/screens/Swap/hooks/useSwapWarning.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts index 014b22d4a56..ce46ee0bec5 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapWarning.ts @@ -178,6 +178,7 @@ export const useSwapWarning = ({ }), (current, previous) => { if (!current.inputAsset || !current.outputAsset) { + updateWarning({ type: SwapWarningType.none, title: '', color: colorMap[SwapWarningType.none], icon: '', subtitle: '' }); return; } From 1eca82b91be8d9cf11e2e3ecfbcfce807828e2f7 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 16:18:23 -0400 Subject: [PATCH 07/14] add more resets --- src/__swaps__/screens/Swap/components/UserAssetsSync.tsx | 5 ++++- src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts | 6 ++++++ src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx b/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx index 6784ae0d04c..743fc955633 100644 --- a/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx +++ b/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx @@ -9,6 +9,8 @@ import { ParsedSearchAsset } from '@/__swaps__/types/assets'; export const UserAssetsSync = () => { const { accountAddress: currentAddress, nativeCurrency: currentCurrency } = useAccountSettings(); + // TODO: Should we setAsset here as well? + // probably only if they aren't on the SWAP screen... useUserAssets( { address: currentAddress as Hex, @@ -20,12 +22,13 @@ export const UserAssetsSync = () => { data, selector: selectUserAssetsList, }), - onSuccess: data => { + onSuccess: (data = []) => { userAssetsStore.setState({ userAssetsById: new Set(data.map(d => d.uniqueId)), userAssets: new Map(data.map(d => [d.uniqueId, d as ParsedSearchAsset])), }); }, + enabled: !!currentAddress, } ); diff --git a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts index 785de80d78a..4f28a2baa0c 100644 --- a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts +++ b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts @@ -49,6 +49,12 @@ export const useCleanupOnExit = () => { isFetching.value = false; isQuoteStale.value = 0; + // reset inputValues and method + SwapInputsController.inputMethod.value = 'slider'; + SwapInputsController.inputValues.value.outputAmount = 0; + SwapInputsController.inputValues.value.outputNativeValue = 0; + + // reset slider position to initial position sliderXPosition.value = SLIDER_WIDTH * INITIAL_SLIDER_POSITION; sliderPressProgress.value = SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT; }; diff --git a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts index df5d5cdfdae..60c4cc8bae5 100644 --- a/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts +++ b/src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts @@ -143,6 +143,7 @@ export function useSwapInputsController({ }); const formattedOutputNativeValue = useDerivedValue(() => { + console.log(inputMethod.value, inputValues.value.outputNativeValue); if ((inputMethod.value === 'slider' && percentageToSwap.value === 0) || !inputValues.value.outputNativeValue) { return '$0.00'; } From f45e15361a89b5effc62f47af7b3fdf51a53cd9d Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Thu, 23 May 2024 17:06:29 -0400 Subject: [PATCH 08/14] keep inputAsset in sync with user assets in background --- .../Swap/components/UserAssetsSync.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx b/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx index 743fc955633..f4ce00b882f 100644 --- a/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx +++ b/src/__swaps__/screens/Swap/components/UserAssetsSync.tsx @@ -5,8 +5,16 @@ import { selectUserAssetsList, selectorFilterByUserChains } from '@/__swaps__/sc import { Hex } from 'viem'; import { userAssetsStore } from '@/state/assets/userAssets'; import { ParsedSearchAsset } from '@/__swaps__/types/assets'; +import { useRoute } from '@react-navigation/native'; +import Routes from '@/navigation/routesNames'; +import { parseSearchAsset } from '@/__swaps__/utils/assets'; +import { SearchAsset } from '@/__swaps__/types/search'; +import { useSwapContext } from '../providers/swap-provider'; +import { SwapAssetType } from '@/__swaps__/types/swap'; export const UserAssetsSync = () => { + const { name } = useRoute(); + const { setAsset } = useSwapContext(); const { accountAddress: currentAddress, nativeCurrency: currentCurrency } = useAccountSettings(); // TODO: Should we setAsset here as well? @@ -27,6 +35,17 @@ export const UserAssetsSync = () => { userAssetsById: new Set(data.map(d => d.uniqueId)), userAssets: new Map(data.map(d => [d.uniqueId, d as ParsedSearchAsset])), }); + + if (name !== Routes.SWAP) { + const [firstAsset] = data; + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: firstAsset as unknown as SearchAsset, // NOTE: We don't really care about this since it's a userAsset + userAsset: firstAsset, + }); + + setAsset({ asset: parsedAsset, type: SwapAssetType.inputAsset }); + } }, enabled: !!currentAddress, } From 10db4d935d9c52fd0fe41f947365bb136c88ffbb Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 09:54:08 -0400 Subject: [PATCH 09/14] fix naming inconsistency in chain selector --- .../screens/Swap/components/TokenList/ChainSelection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx b/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx index 658900de040..720aa19faf0 100644 --- a/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx +++ b/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx @@ -49,10 +49,10 @@ export const ChainSelection = ({ allText, output }: ChainSelectionProps) => { const chainName = useSharedValue( output - ? chainNameFromChainIdWorklet(selectedOutputChainId.value) + ? chainNameForChainIdWithMainnetSubstitutionWorklet(selectedOutputChainId.value) : initialFilter === 'all' ? allText - : chainNameFromChainIdWorklet(initialFilter as ChainId) + : chainNameForChainIdWithMainnetSubstitutionWorklet(initialFilter as ChainId) ); useAnimatedReaction( From ba06ff3ec51272bff2a0645d7d36bbbaf2a67835 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 12:02:32 -0400 Subject: [PATCH 10/14] cleanup --- .../components/CleanupAssetsOnUnmount.tsx | 12 +---- .../screens/Swap/hooks/usePrefillAssets.ts | 53 ------------------- 2 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts diff --git a/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx b/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx index d77c2ad6129..31dbf35b475 100644 --- a/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx +++ b/src/__swaps__/screens/Swap/components/CleanupAssetsOnUnmount.tsx @@ -1,16 +1,6 @@ import { useCleanupOnExit } from '../hooks/useCleanupOnExit'; -import { useSwapContext } from '../providers/swap-provider'; export const CleanupAssetsOnUnmount = () => { - const { SwapInputsController, inputProgress, outputProgress, internalSelectedInputAsset, internalSelectedOutputAsset } = useSwapContext(); - - useCleanupOnExit({ - SwapInputsController, - inputProgress, - outputProgress, - internalSelectedInputAsset, - internalSelectedOutputAsset, - }); - + useCleanupOnExit(); return null; }; diff --git a/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts b/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts deleted file mode 100644 index 9fcfaad68de..00000000000 --- a/src/__swaps__/screens/Swap/hooks/usePrefillAssets.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { useEffect } from 'react'; - -import { RootStackParamList } from '@/navigation/types'; -import { RouteProp, useRoute } from '@react-navigation/native'; -import { SwapAssetType } from '@/__swaps__/types/swap'; -import { ParsedSearchAsset } from '@/__swaps__/types/assets'; -import { userAssetsStore } from '@/state/assets/userAssets'; -import { parseSearchAsset } from '@/__swaps__/utils/assets'; -import { SearchAsset } from '@/__swaps__/types/search'; - -export const usePrefillAssets = ({ - setAsset, -}: { - setAsset: ({ type, asset }: { type: SwapAssetType; asset: ParsedSearchAsset }) => void; -}) => { - const { params = {} } = useRoute>(); - - const { inputAssetUniqueId, outputAssetUniqueId } = params; - - useEffect(() => { - if (inputAssetUniqueId) { - // TODO: do we need to handle the case where the input asset isn't defined? - const userAsset = userAssetsStore.getState().getUserAsset(inputAssetUniqueId); - const parsedAsset = parseSearchAsset({ - assetWithPrice: undefined, - searchAsset: userAsset as SearchAsset, - userAsset, - }); - - setAsset({ - type: SwapAssetType.inputAsset, - asset: parsedAsset, - }); - } - - if (outputAssetUniqueId) { - // TODO: do we need to handle the case where the output asset isn't defined? - const userAsset = userAssetsStore.getState().getUserAsset(outputAssetUniqueId); - const parsedAsset = parseSearchAsset({ - assetWithPrice: undefined, - searchAsset: userAsset as SearchAsset, - userAsset, - }); - - setAsset({ - type: SwapAssetType.outputAsset, - asset: parsedAsset, - }); - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); -}; From 3211d3a6ecd9ba65b5f03cdc424bae4dc9dcac10 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 12:12:49 -0400 Subject: [PATCH 11/14] fix chain name in asset to sell list not changing --- .../components/TokenList/ChainSelection.tsx | 6 +-- .../screens/Swap/hooks/useCleanupOnExit.ts | 54 +------------------ .../screens/Swap/providers/swap-provider.tsx | 42 +++++++++++++++ src/navigation/Routes.android.tsx | 12 +---- src/navigation/Routes.ios.tsx | 12 +---- 5 files changed, 48 insertions(+), 78 deletions(-) diff --git a/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx b/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx index 720aa19faf0..1638ab8e31c 100644 --- a/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx +++ b/src/__swaps__/screens/Swap/components/TokenList/ChainSelection.tsx @@ -74,12 +74,10 @@ export const ChainSelection = ({ allText, output }: ChainSelectionProps) => { userAssetsStore.setState({ filter: actionKey === 'all' ? 'all' : (Number(actionKey) as ChainId), }); - runOnUI(() => { - chainName.value = actionKey === 'all' ? allText : chainNameForChainIdWithMainnetSubstitutionWorklet(Number(actionKey) as ChainId); - }); + chainName.value = actionKey === 'all' ? allText : chainNameForChainIdWithMainnetSubstitutionWorklet(Number(actionKey) as ChainId); } }, - [allText, chainName, output, selectedOutputChainId] + [allText, chainName, output, setSelectedOutputChainId] ); const menuConfig = useMemo(() => { diff --git a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts index 4f28a2baa0c..c3b2b259ddf 100644 --- a/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts +++ b/src/__swaps__/screens/Swap/hooks/useCleanupOnExit.ts @@ -1,62 +1,12 @@ import { useEffect } from 'react'; -import { swapsStore } from '@/state/swaps/swapsStore'; -import { userAssetsStore } from '@/state/assets/userAssets'; -import { parseSearchAsset } from '@/__swaps__/utils/assets'; import { useSwapContext } from '../providers/swap-provider'; -import { SwapAssetType } from '@/__swaps__/types/swap'; -import { INITIAL_SLIDER_POSITION, SLIDER_COLLAPSED_HEIGHT, SLIDER_HEIGHT, SLIDER_WIDTH } from '../constants'; export const useCleanupOnExit = () => { - const { - setAsset, - quote, - isFetching, - isQuoteStale, - sliderXPosition, - sliderPressProgress, - lastTypedInput, - focusedInput, - internalSelectedOutputAsset, - SwapInputsController, - } = useSwapContext(); + const { reset } = useSwapContext(); useEffect(() => { return () => { - const firstUserAsset = userAssetsStore.getState().userAssets.values().next().value; - const parsedAsset = parseSearchAsset({ - assetWithPrice: undefined, - searchAsset: firstUserAsset, - userAsset: firstUserAsset, - }); - - // reset back to the user's asset with largest balance - setAsset({ - type: SwapAssetType.inputAsset, - asset: parsedAsset, - }); - - // reset output asset to null - internalSelectedOutputAsset.value = null; - swapsStore.setState({ outputAsset: null }); - - // reset input states - lastTypedInput.value = 'inputAmount'; - focusedInput.value = 'inputAmount'; - - // stop quote fetching interval if it was set - SwapInputsController.quoteFetchingInterval.stop(); - quote.value = null; - isFetching.value = false; - isQuoteStale.value = 0; - - // reset inputValues and method - SwapInputsController.inputMethod.value = 'slider'; - SwapInputsController.inputValues.value.outputAmount = 0; - SwapInputsController.inputValues.value.outputNativeValue = 0; - - // reset slider position to initial position - sliderXPosition.value = SLIDER_WIDTH * INITIAL_SLIDER_POSITION; - sliderPressProgress.value = SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT; + reset(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/__swaps__/screens/Swap/providers/swap-provider.tsx b/src/__swaps__/screens/Swap/providers/swap-provider.tsx index 53445323b0d..93f4bbb260f 100644 --- a/src/__swaps__/screens/Swap/providers/swap-provider.tsx +++ b/src/__swaps__/screens/Swap/providers/swap-provider.tsx @@ -63,6 +63,8 @@ interface SwapContextType { confirmButtonIcon: Readonly>; confirmButtonLabel: Readonly>; confirmButtonIconStyle: StyleProp; + + reset: () => void; } const SwapContext = createContext(undefined); @@ -316,6 +318,44 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { }; }); + const reset = () => { + const firstUserAsset = userAssetsStore.getState().userAssets.values().next().value; + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: firstUserAsset, + userAsset: firstUserAsset, + }); + + // reset back to the user's asset with largest balance + setAsset({ + type: SwapAssetType.inputAsset, + asset: parsedAsset, + }); + + // reset output asset to null + internalSelectedOutputAsset.value = null; + swapsStore.setState({ outputAsset: null }); + + // reset input states + lastTypedInput.value = 'inputAmount'; + focusedInput.value = 'inputAmount'; + + // stop quote fetching interval if it was set + SwapInputsController.quoteFetchingInterval.stop(); + quote.value = null; + isFetching.value = false; + isQuoteStale.value = 0; + + // reset inputValues and method + SwapInputsController.inputMethod.value = 'slider'; + SwapInputsController.inputValues.value.outputAmount = 0; + SwapInputsController.inputValues.value.outputNativeValue = 0; + + // reset slider position to initial position + sliderXPosition.value = SLIDER_WIDTH * INITIAL_SLIDER_POSITION; + sliderPressProgress.value = SLIDER_COLLAPSED_HEIGHT / SLIDER_HEIGHT; + }; + console.log('re-rendered swap provider: ', Date.now()); return ( @@ -355,6 +395,8 @@ export const SwapProvider = ({ children }: SwapProviderProps) => { confirmButtonIcon, confirmButtonLabel, confirmButtonIconStyle, + + reset, }} > {children} diff --git a/src/navigation/Routes.android.tsx b/src/navigation/Routes.android.tsx index 70b4b99cf9b..e15bb0e8539 100644 --- a/src/navigation/Routes.android.tsx +++ b/src/navigation/Routes.android.tsx @@ -140,16 +140,6 @@ function MainOuterNavigator() { ); } -function SwapNavigator() { - return ( - - - - - - ); -} - function BSNavigator() { const remoteConfig = useRemoteConfig(); const profilesEnabled = useExperimentalFlag(PROFILES); @@ -256,7 +246,7 @@ function BSNavigator() { - {swapsV2Enabled && } + {swapsV2Enabled && } ); } diff --git a/src/navigation/Routes.ios.tsx b/src/navigation/Routes.ios.tsx index ab5a1975dbd..07a51f9bde9 100644 --- a/src/navigation/Routes.ios.tsx +++ b/src/navigation/Routes.ios.tsx @@ -141,16 +141,6 @@ function MainStack() { ); } -function SwapsNavigator() { - return ( - - - - - - ); -} - function NativeStackNavigator() { const remoteConfig = useRemoteConfig(); const { colors, isDarkMode } = useTheme(); @@ -306,7 +296,7 @@ function NativeStackNavigator() { - {swapsV2Enabled && } + {swapsV2Enabled && } ); } From e7d3a4b7364fdeb7f3ee2ef62479b28c715f04ce Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 13:40:14 -0400 Subject: [PATCH 12/14] add prefill entry point from asset charts --- .../screens/Swap/components/SwapNumberPad.tsx | 4 +- .../sheet-action-buttons/SwapActionButton.js | 44 +++++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx index 45a7a8e0481..64105c8c476 100644 --- a/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx +++ b/src/__swaps__/screens/Swap/components/SwapNumberPad.tsx @@ -32,14 +32,14 @@ type numberPadCharacter = number | 'backspace' | '.'; export const SwapNumberPad = () => { const { isDarkMode } = useColorMode(); - const { focusedInput, isQuoteStale, SwapInputController, configProgress } = useSwapContext(); + const { focusedInput, isQuoteStale, SwapInputsController, configProgress } = useSwapContext(); const longPressTimer = useSharedValue(0); const addNumber = (number?: number) => { 'worklet'; // immediately stop the quote fetching interval - SwapInputController.quoteFetchingInterval.stop(); + SwapInputsController.quoteFetchingInterval.stop(); isQuoteStale.value = 1; const inputKey = focusedInput.value; diff --git a/src/components/sheet/sheet-action-buttons/SwapActionButton.js b/src/components/sheet/sheet-action-buttons/SwapActionButton.js index 62b7909292a..44b936a8c76 100644 --- a/src/components/sheet/sheet-action-buttons/SwapActionButton.js +++ b/src/components/sheet/sheet-action-buttons/SwapActionButton.js @@ -1,26 +1,40 @@ import lang from 'i18n-js'; import React, { useCallback } from 'react'; import SheetActionButton from './SheetActionButton'; -import { CurrencySelectionTypes, ExchangeModalTypes } from '@/helpers'; -import AssetInputTypes from '@/helpers/assetInputTypes'; -import { useExpandedStateNavigation, useSwapCurrencyHandlers } from '@/hooks'; +import { useExpandedStateNavigation } from '@/hooks'; import Routes from '@/navigation/routesNames'; -import { ethereumUtils } from '@/utils'; +import { useRemoteConfig } from '@/model/remoteConfig'; +import { SWAPS_V2, useExperimentalFlag } from '@/config'; +import assetInputTypes from '@/helpers/assetInputTypes'; +import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider'; +import { SwapAssetType } from '@/__swaps__/types/swap'; function SwapActionButton({ asset, color: givenColor, inputType, label, fromDiscover, weight = 'heavy', ...props }) { + const { setAsset } = useSwapContext(); + const { swaps_v2 } = useRemoteConfig(); + const { navigate } = useNavigation(); + const swapsV2Enabled = useExperimentalFlag(SWAPS_V2); + const { colors } = useTheme(); const color = givenColor || colors.swapPurple; - const { updateInputCurrency, updateOutputCurrency } = useSwapCurrencyHandlers({ - defaultInputAsset: inputType === AssetInputTypes.in ? asset : null, - defaultOutputAsset: inputType === AssetInputTypes.out ? asset : null, - shouldUpdate: true, - type: ExchangeModalTypes.swap, - }); - - const navigate = useExpandedStateNavigation(inputType, fromDiscover, asset); + const old_navigate = useExpandedStateNavigation(inputType, fromDiscover, asset); const goToSwap = useCallback(() => { - navigate(Routes.EXCHANGE_MODAL, params => { + if (swapsV2Enabled || swaps_v2) { + if (inputType === assetInputTypes.in) { + setAsset({ type: SwapAssetType.inputAsset, asset }); + } else { + setAsset({ type: SwapAssetType.outputAsset, asset }); + } + + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); + }); + + return; + } + + old_navigate(Routes.EXCHANGE_MODAL, params => { if (params.outputAsset) { return { params: { @@ -40,7 +54,7 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc }; } }); - }, [asset, navigate]); + }, [asset, inputType, navigate, old_navigate, setAsset, swapsV2Enabled, swaps_v2]); return ( ); } +import { useNavigation } from '@/navigation'; +import { InteractionManager } from 'react-native'; export default React.memo(SwapActionButton); From 40efd30c33df2f79568b86a68d0de4577f8a6ea9 Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 14:04:33 -0400 Subject: [PATCH 13/14] stash changes --- .../sheet-action-buttons/SwapActionButton.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/sheet/sheet-action-buttons/SwapActionButton.js b/src/components/sheet/sheet-action-buttons/SwapActionButton.js index 44b936a8c76..ccd33d26fc3 100644 --- a/src/components/sheet/sheet-action-buttons/SwapActionButton.js +++ b/src/components/sheet/sheet-action-buttons/SwapActionButton.js @@ -21,10 +21,20 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc const old_navigate = useExpandedStateNavigation(inputType, fromDiscover, asset); const goToSwap = useCallback(() => { if (swapsV2Enabled || swaps_v2) { + const chainId = ethereumUtils.getChainIdFromNetwork(asset.network); + const userAsset = userAssetsStore.getState().userAssets.get(`${asset.address}_${chainId}`); + const parsedAsset = parseSearchAsset({ + assetWithPrice: undefined, + searchAsset: asset, + userAsset, + }); + + console.log(parsedAsset); + if (inputType === assetInputTypes.in) { - setAsset({ type: SwapAssetType.inputAsset, asset }); + setAsset({ type: SwapAssetType.inputAsset, asset: parsedAsset }); } else { - setAsset({ type: SwapAssetType.outputAsset, asset }); + setAsset({ type: SwapAssetType.outputAsset, asset: parsedAsset }); } InteractionManager.runAfterInteractions(() => { @@ -70,5 +80,8 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc } import { useNavigation } from '@/navigation'; import { InteractionManager } from 'react-native'; +import { parseSearchAsset } from '@/__swaps__/utils/assets'; +import { userAssetsStore } from '@/state/assets/userAssets'; +import { ethereumUtils } from '@/utils'; export default React.memo(SwapActionButton); From d45fdfffac172e5212cb15f44f886a75e3c2ee6d Mon Sep 17 00:00:00 2001 From: Matthew Wall Date: Fri, 24 May 2024 14:50:03 -0400 Subject: [PATCH 14/14] wrap in InteractionManager --- .../profile-header/ProfileActionButtonsRow.tsx | 6 ++++-- .../sheet/sheet-action-buttons/SwapActionButton.js | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx index bcc32b81ec5..0a509da6adb 100644 --- a/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx +++ b/src/components/asset-list/RecyclerAssetList2/profile-header/ProfileActionButtonsRow.tsx @@ -1,7 +1,7 @@ import Clipboard from '@react-native-clipboard/clipboard'; import lang from 'i18n-js'; import * as React from 'react'; -import { PressableProps } from 'react-native'; +import { InteractionManager, PressableProps } from 'react-native'; import Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated'; import { ButtonPressAnimation } from '@/components/animations'; import { CopyFloatingEmojis } from '@/components/floating-emojis'; @@ -180,7 +180,9 @@ function SwapButton() { android && delayNext(); if (swapsV2Enabled) { - navigate(Routes.SWAP); + InteractionManager.runAfterInteractions(() => { + navigate(Routes.SWAP); + }); return; } diff --git a/src/components/sheet/sheet-action-buttons/SwapActionButton.js b/src/components/sheet/sheet-action-buttons/SwapActionButton.js index ccd33d26fc3..130b5a46da6 100644 --- a/src/components/sheet/sheet-action-buttons/SwapActionButton.js +++ b/src/components/sheet/sheet-action-buttons/SwapActionButton.js @@ -24,13 +24,11 @@ function SwapActionButton({ asset, color: givenColor, inputType, label, fromDisc const chainId = ethereumUtils.getChainIdFromNetwork(asset.network); const userAsset = userAssetsStore.getState().userAssets.get(`${asset.address}_${chainId}`); const parsedAsset = parseSearchAsset({ - assetWithPrice: undefined, - searchAsset: asset, + assetWithPrice: asset, + searchAsset: { ...asset, chainId }, userAsset, }); - console.log(parsedAsset); - if (inputType === assetInputTypes.in) { setAsset({ type: SwapAssetType.inputAsset, asset: parsedAsset }); } else {