Skip to content

Commit

Permalink
Revert "swap button states (#5785)"
Browse files Browse the repository at this point in the history
This reverts commit fc0ff05.
  • Loading branch information
jinchung committed Jun 15, 2024
1 parent 208856b commit 69d49bf
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 292 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ButtonPressAnimation } from '@/components/animations';
import { IS_IOS } from '@/env';
import ConditionalWrap from 'conditional-wrap';
import React from 'react';
import { StyleProp, ViewProps, ViewStyle } from 'react-native';
import { TapGestureHandler, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import Animated, { AnimatedStyle, runOnJS, useAnimatedGestureHandler } from 'react-native-reanimated';
import { ButtonPressAnimation } from '@/components/animations';
import { IS_IOS } from '@/env';

export type GestureHandlerButtonProps = {
buttonPressWrapperStyleIOS?: StyleProp<ViewStyle>;
Expand Down
35 changes: 11 additions & 24 deletions src/__swaps__/screens/Swap/components/SwapActionButton.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
/* eslint-disable no-nested-ternary */
import React from 'react';
import { StyleProp, StyleSheet, TextStyle, ViewProps, ViewStyle } from 'react-native';
import Animated, { DerivedValue, useAnimatedProps, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { StyleProp, StyleSheet, TextStyle, ViewStyle } from 'react-native';
import Animated, { DerivedValue, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';

import { AnimatedText, Box, Column, Columns, globalColors, useColorMode, useForegroundColor } from '@/design-system';
import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets';
import { getColorValueForThemeWorklet } from '@/__swaps__/utils/swaps';
import { AnimatedText, Box, Column, Columns, globalColors, useColorMode, useForegroundColor } from '@/design-system';
import { GestureHandlerV1Button } from './GestureHandlerV1Button';

const AnimatedGestureHandlerV1Button = Animated.createAnimatedComponent(GestureHandlerV1Button);

export const SwapActionButton = ({
asset,
borderRadius,
Expand All @@ -25,8 +23,6 @@ export const SwapActionButton = ({
scaleTo,
small,
style,
disabled,
isLoading,
}: {
asset: DerivedValue<ExtendedAnimatedAssetWithColors | null>;
borderRadius?: number;
Expand All @@ -42,8 +38,6 @@ export const SwapActionButton = ({
scaleTo?: number;
small?: boolean;
style?: ViewStyle;
disabled?: DerivedValue<boolean | undefined>;
isLoading?: DerivedValue<boolean | undefined>;
}) => {
const { isDarkMode } = useColorMode();
const fallbackColor = useForegroundColor('label');
Expand Down Expand Up @@ -86,8 +80,6 @@ export const SwapActionButton = ({
},
shadowOpacity: isDarkMode ? 0.2 : small ? 0.2 : 0.36,
shadowRadius: isDarkMode ? 26 : small ? 9 : 15,
// we don't want to change the opacity when it's loading
opacity: !isLoading?.value && disabled?.value ? 0.6 : 1,
};
});

Expand All @@ -105,20 +97,15 @@ export const SwapActionButton = ({
return rightIcon;
});

const buttonAnimatedProps = useAnimatedProps(() => {
return {
pointerEvents: (disabled?.value ? 'none' : 'box-only') as ViewProps['pointerEvents'],
disableButtonPressWrapper: disabled?.value,
scaleTo: scaleTo || (hugContent ? undefined : 0.925),
};
});

return (
<AnimatedGestureHandlerV1Button
animatedProps={buttonAnimatedProps}
onPressStartWorklet={onPressWorklet}
<GestureHandlerV1Button
onPressJS={onPressJS}
style={[hugContent && feedActionButtonStyles.buttonWrapper, style]}
onPressWorklet={onPressWorklet}
scaleTo={scaleTo || (hugContent ? undefined : 0.925)}
style={{
...(hugContent && feedActionButtonStyles.buttonWrapper),
...(style || {}),
}}
>
<Box
as={Animated.View}
Expand Down Expand Up @@ -151,7 +138,7 @@ export const SwapActionButton = ({
)}
</Columns>
</Box>
</AnimatedGestureHandlerV1Button>
</GestureHandlerV1Button>
);
};

Expand Down
24 changes: 13 additions & 11 deletions src/__swaps__/screens/Swap/components/SwapBottomPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { StyleSheet } from 'react-native';
import { getSoftMenuBarHeight } from 'react-native-extra-dimensions-android';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';
import Animated, { useAnimatedStyle, withSpring } from 'react-native-reanimated';

import { Box, Column, Columns, Separator, globalColors, useColorMode } from '@/design-system';
import { safeAreaInsetValues } from '@/utils';
Expand All @@ -21,8 +21,15 @@ import { SwapActionButton } from './SwapActionButton';

export function SwapBottomPanel() {
const { isDarkMode } = useColorMode();
const { confirmButtonIconStyle, confirmButtonProps, internalSelectedOutputAsset, AnimatedSwapStyles, SwapNavigation, configProgress } =
useSwapContext();
const {
confirmButtonIcon,
confirmButtonIconStyle,
confirmButtonLabel,
internalSelectedOutputAsset,
AnimatedSwapStyles,
SwapNavigation,
configProgress,
} = useSwapContext();

const { swipeToDismissGestureHandler, gestureY } = useBottomPanelGestureHandler();

Expand All @@ -43,10 +50,6 @@ export function SwapBottomPanel() {
};
});

const icon = useDerivedValue(() => confirmButtonProps.value.icon);
const label = useDerivedValue(() => confirmButtonProps.value.label);
const disabled = useDerivedValue(() => confirmButtonProps.value.disabled);

return (
// @ts-expect-error Property 'children' does not exist on type
<PanGestureHandler maxPointers={1} onGestureEvent={swipeToDismissGestureHandler}>
Expand Down Expand Up @@ -77,12 +80,11 @@ export function SwapBottomPanel() {
</Box>
</Column>
<SwapActionButton
onPressWorklet={SwapNavigation.handleSwapAction}
asset={internalSelectedOutputAsset}
icon={icon}
icon={confirmButtonIcon}
iconStyle={confirmButtonIconStyle}
label={label}
disabled={disabled}
label={confirmButtonLabel}
onPressWorklet={SwapNavigation.handleSwapAction}
scaleTo={0.9}
/>
</Columns>
Expand Down
8 changes: 4 additions & 4 deletions src/__swaps__/screens/Swap/components/SwapInputAsset.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import MaskedView from '@react-native-masked-view/masked-view';
import React from 'react';
import { StatusBar, StyleSheet } from 'react-native';
import { StyleSheet, StatusBar } from 'react-native';
import Animated, { useDerivedValue } from 'react-native-reanimated';
import { ScreenCornerRadius } from 'react-native-screen-corner-radius';

import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';

import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { GestureHandlerV1Button } from '@/__swaps__/screens/Swap/components/GestureHandlerV1Button';
import { SwapActionButton } from '@/__swaps__/screens/Swap/components/SwapActionButton';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { SwapInput } from '@/__swaps__/screens/Swap/components/SwapInput';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenList';
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { IS_ANDROID } from '@/env';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { AnimatedSwapCoinIcon } from './AnimatedSwapCoinIcon';
import * as i18n from '@/languages';

Expand Down
17 changes: 8 additions & 9 deletions src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';
import MaskedView from '@react-native-masked-view/masked-view';
import React, { useCallback } from 'react';
import { StatusBar, StyleSheet } from 'react-native';
import { StyleSheet, StatusBar } from 'react-native';
import Animated, { runOnJS, useDerivedValue } from 'react-native-reanimated';
import { ScreenCornerRadius } from 'react-native-screen-corner-radius';

import { AnimatedSwapCoinIcon } from '@/__swaps__/screens/Swap/components/AnimatedSwapCoinIcon';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { AnimatedText, Box, Column, Columns, Stack, useColorMode } from '@/design-system';
import { GestureHandlerV1Button } from '@/__swaps__/screens/Swap/components/GestureHandlerV1Button';
import { SwapActionButton } from '@/__swaps__/screens/Swap/components/SwapActionButton';
import { FadeMask } from '@/__swaps__/screens/Swap/components/FadeMask';
import { SwapInput } from '@/__swaps__/screens/Swap/components/SwapInput';
import { BalanceBadge } from '@/__swaps__/screens/Swap/components/BalanceBadge';
import { AnimatedSwapCoinIcon } from '@/__swaps__/screens/Swap/components/AnimatedSwapCoinIcon';
import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenList';
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { ChainId } from '@/__swaps__/types/chains';
import { IS_ANDROID } from '@/env';
import * as i18n from '@/languages';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { useNavigation } from '@/navigation';
import Routes from '@/navigation/routesNames';
import { useSwapsStore } from '@/state/swaps/swapsStore';
import { ethereumUtils } from '@/utils';
import { ChainId } from '@/__swaps__/types/chains';
import * as i18n from '@/languages';

const SELECT_LABEL = i18n.t(i18n.l.swap.select);
const NO_BALANCE_LABEL = i18n.t(i18n.l.swap.no_balance);
Expand Down
70 changes: 55 additions & 15 deletions src/__swaps__/screens/Swap/hooks/useEstimatedGasFee.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { greaterThanWorklet, toScaledIntegerWorklet } from '@/__swaps__/safe-math/SafeMath';
import { ChainId } from '@/__swaps__/types/chains';
import { weiToGwei } from '@/__swaps__/utils/ethereum';
import { add, convertAmountToNativeDisplayWorklet, formatNumber, multiply } from '@/__swaps__/utils/numbers';
import ethereumUtils, { useNativeAssetForNetwork } from '@/utils/ethereumUtils';
import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
import { useDebouncedCallback } from 'use-debounce';
import { formatUnits } from 'viem';

import { useSyncedSwapQuoteStore } from '../providers/SyncSwapStateAndSharedValues';
import { useSwapContext } from '../providers/swap-provider';
import { GasSettings } from './useCustomGas';
import { useSwapEstimatedGasLimit } from './useSwapEstimatedGasLimit';
import { useAccountSettings } from '@/hooks';
Expand All @@ -18,11 +20,6 @@ function safeBigInt(value: string) {
}
}

export function calculateGasFee(gasSettings: GasSettings, gasLimit: string) {
const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;
return multiply(gasLimit, amount);
}

export function useEstimatedGasFee({
chainId,
gasLimit,
Expand All @@ -39,23 +36,66 @@ export function useEstimatedGasFee({
return useMemo(() => {
if (!gasLimit || !gasSettings || !nativeNetworkAsset?.price) return;

const fee = calculateGasFee(gasSettings, gasLimit);
const amount = gasSettings.isEIP1559 ? add(gasSettings.maxBaseFee, gasSettings.maxPriorityFee) : gasSettings.gasPrice;

const totalWei = multiply(gasLimit, amount);
const networkAssetPrice = nativeNetworkAsset.price.value?.toString();

if (!networkAssetPrice) return `${formatNumber(weiToGwei(fee))} Gwei`;
if (!networkAssetPrice) return `${formatNumber(weiToGwei(totalWei))} Gwei`;

const feeFormatted = formatUnits(safeBigInt(fee), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, feeFormatted);
const gasAmount = formatUnits(safeBigInt(totalWei), nativeNetworkAsset.decimals).toString();
const feeInUserCurrency = multiply(networkAssetPrice, gasAmount);

return convertAmountToNativeDisplayWorklet(feeInUserCurrency, nativeCurrency);
}, [gasLimit, gasSettings, nativeCurrency, nativeNetworkAsset]);
}

export function useSwapEstimatedGasFee(gasSettings: GasSettings | undefined) {
const { assetToSell, chainId = ChainId.mainnet, quote } = useSyncedSwapQuoteStore();
const { data: estimatedGasLimit, isFetching } = useSwapEstimatedGasLimit({ chainId, assetToSell, quote });
const { internalSelectedInputAsset: assetToSell, internalSelectedOutputAsset: assetToBuy, quote } = useSwapContext();

const [state, setState] = useState({
assetToBuy: assetToBuy.value,
assetToSell: assetToSell.value,
chainId: assetToSell.value?.chainId ?? ChainId.mainnet,
quote: quote.value,
});

const debouncedStateSet = useDebouncedCallback(setState, 100, { leading: false, trailing: true });

// Updates the state as a single block in response to quote changes to ensure the gas fee is cleanly updated once
useAnimatedReaction(
() => quote.value,
(current, previous) => {
if (!assetToSell.value || !assetToBuy.value || !current || !previous || 'error' in current) return;

const isSwappingMoreThanAvailableBalance = greaterThanWorklet(
current.sellAmount.toString(),
toScaledIntegerWorklet(assetToSell.value.balance.amount, assetToSell.value.decimals)
);

// Skip gas fee recalculation if the user is trying to swap more than their available balance, as it isn't
// needed and was previously resulting in errors in useEstimatedGasFee.
if (isSwappingMoreThanAvailableBalance) return;

if (current !== previous) {
runOnJS(debouncedStateSet)({
assetToBuy: assetToBuy.value,
assetToSell: assetToSell.value,
chainId: assetToSell.value?.chainId ?? ChainId.mainnet,
quote: current,
});
}
}
);

const { data: gasLimit, isFetching } = useSwapEstimatedGasLimit(
{ chainId: state.chainId, quote: state.quote, assetToSell: state.assetToSell },
{
enabled: !!state.quote && !!state.assetToSell && !!state.assetToBuy && !('error' in quote),
}
);

const estimatedFee = useEstimatedGasFee({ chainId, gasLimit: estimatedGasLimit, gasSettings });
const estimatedFee = useEstimatedGasFee({ chainId: state.chainId, gasLimit, gasSettings });

return useMemo(() => ({ isLoading: isFetching, data: estimatedFee }), [estimatedFee, isFetching]);
}
Loading

0 comments on commit 69d49bf

Please sign in to comment.