Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review Sheet #5632

Merged
merged 11 commits into from
Apr 19, 2024
12 changes: 8 additions & 4 deletions src/__swaps__/screens/Swap/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { navbarHeight } from '@/components/navbar/Navbar';
import { Box } from '@/design-system';
import { safeAreaInsetValues } from '@/utils';

import { SheetGestureBlocker } from '@/__swaps__/screens/Swap/components/SheetGestureBlocker';
import { SwapSheetGestureBlocker } from '@/__swaps__/screens/Swap/components/SwapSheetGestureBlocker';
import { SwapBackground } from '@/__swaps__/screens/Swap/components/SwapBackground';
import { FlipButton } from '@/__swaps__/screens/Swap/components/FlipButton';
import { ExchangeRateBubble } from '@/__swaps__/screens/Swap/components/ExchangeRateBubble';
import { SwapInputAsset } from '@/__swaps__/screens/Swap/components/controls/SwapInputAsset';
import { SwapOutputAsset } from '@/__swaps__/screens/Swap/components/controls/SwapOutputAsset';
import { SwapNavbar } from '@/__swaps__/screens/Swap/components/SwapNavbar';
import { SwapAmountInputs } from '@/__swaps__/screens/Swap/components/controls/SwapAmountInputs';
import { SwapActions } from '@/__swaps__/screens/Swap/components/controls/SwapActions';
import { SwapWarning } from './components/SwapWarning';

/** README
Expand Down Expand Up @@ -57,23 +58,26 @@ import { SwapWarning } from './components/SwapWarning';

export function SwapScreen() {
return (
<SheetGestureBlocker>
<SwapSheetGestureBlocker>
<Box as={Page} style={styles.rootViewBackground} testID="swap-screen" width="full">
<SwapBackground />
<Box alignItems="center" height="full" paddingTop={{ custom: safeAreaInsetValues.top + (navbarHeight - 12) + 29 }} width="full">
<SwapInputAsset />
<FlipButton />
<SwapOutputAsset />
<Box width="full" position="absolute" bottom="0px">
<SwapAmountInputs />
<SwapActions />
</Box>
<Box alignItems="center" justifyContent="center" style={{ position: 'relative' }}>
<ExchangeRateBubble />
<SwapWarning />
</Box>

<SwapAmountInputs />
</Box>
<SwapNavbar />
</Box>
</SheetGestureBlocker>
</SwapSheetGestureBlocker>
);
}

Expand Down
90 changes: 90 additions & 0 deletions src/__swaps__/screens/Swap/components/AnimatedSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';

import { AnimatedText, Box, Inline, globalColors, useColorMode, useForegroundColor } from '@/design-system';
import Animated, { DerivedValue, useAnimatedStyle, useDerivedValue, withSpring, withTiming } from 'react-native-reanimated';
import { fadeConfig, springConfig } from '../constants';
import { opacityWorklet } from '@/__swaps__/utils/swaps';
import { GestureHandlerButtonProps, GestureHandlerV1Button } from './GestureHandlerV1Button';
import { StyleSheet } from 'react-native';

type AnimatedSwitchProps = {
onToggle: () => void;
value: DerivedValue<boolean>;
activeLabel?: string;
inactiveLabel?: string;
} & Omit<GestureHandlerButtonProps, 'children'>;

export function AnimatedSwitch({ value, onToggle, activeLabel, inactiveLabel, ...props }: AnimatedSwitchProps) {
const { isDarkMode } = useColorMode();

const inactiveBg = useForegroundColor('fillSecondary');
const activeBg = useForegroundColor('green');
const border = useForegroundColor('separatorSecondary');

const containerStyles = useAnimatedStyle(() => {
return {
backgroundColor: !value.value
? withTiming(opacityWorklet(inactiveBg, 0.12), fadeConfig)
: withTiming(opacityWorklet(activeBg, 0.64), fadeConfig),
borderColor: opacityWorklet(border, 0.06),
};
});

const circleStyles = useAnimatedStyle(() => {
return {
transform: [
{
translateX: withSpring(value.value ? 11 : 1, springConfig),
},
],
};
});

const labelItem = useDerivedValue(() => {
if (!activeLabel && !inactiveLabel) {
return;
}

if (value.value) {
return activeLabel;
}

return inactiveLabel;
});

if (labelItem.value) {
return (
<Inline alignVertical="center" horizontalSpace="6px">
<AnimatedText align="right" color={isDarkMode ? 'labelSecondary' : 'label'} size="15pt" weight="heavy" text={labelItem} />
{/* TODO: Small switch, so let's move this out to be the whole row */}
<GestureHandlerV1Button onPressWorklet={onToggle} style={[styles.containerStyles, containerStyles]} {...props}>
<Box style={[styles.circleStyles, circleStyles]} as={Animated.View} />
</GestureHandlerV1Button>
</Inline>
);
}

return (
<GestureHandlerV1Button onPressWorklet={onToggle} style={[styles.containerStyles, containerStyles]} {...props}>
<Box style={[styles.circleStyles, circleStyles]} as={Animated.View} />
</GestureHandlerV1Button>
);
}

const styles = StyleSheet.create({
containerStyles: {
position: 'relative',
borderWidth: 1,
borderRadius: 100,
width: 26,
height: 16,
},
circleStyles: {
top: 1,
backgroundColor: globalColors.white100,
borderRadius: 100,
width: 12,
height: 12,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const ExchangeRateBubble = () => {
});

return (
<ButtonPressAnimation onPress={() => setExchangeRateIndex((exchangeRateIndex + 1) % 4)} scaleTo={0.925} style={{ marginTop: 4 }}>
<ButtonPressAnimation onPress={() => setExchangeRateIndex((exchangeRateIndex + 1) % 4)} scaleTo={0.925}>
<Box
as={Animated.View}
alignItems="center"
Expand Down
80 changes: 76 additions & 4 deletions src/__swaps__/screens/Swap/components/GasButton.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useMemo, useState, useCallback, useRef, useEffect, ReactNode } from 'react';
import { InteractionManager } from 'react-native';
import { ButtonPressAnimation } from '@/components/animations';
import { AnimatedText, Inline, Stack, Text, TextIcon } from '@/design-system';
import { AnimatedText, Box, Inline, Stack, Text, TextIcon, useColorMode, useForegroundColor } from '@/design-system';
import { useGasStore } from '@/state/gas/gasStore';
import { Centered } from '@/components/layout';
import { useTheme } from '@/theme';
Expand All @@ -18,21 +18,27 @@ import { ethereumUtils, gasUtils } from '@/utils';
import styled from '@/styled-thing';
import { useMeteorology } from '@/__swaps__/utils/meteorology';
import { parseGasFeeParamsBySpeed } from '@/__swaps__/utils/gasUtils';
import { useDerivedValue } from 'react-native-reanimated';
import Animated, { useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
import { ParsedAddressAsset } from '@/entities';
import { GasFeeLegacyParamsBySpeed, GasFeeParamsBySpeed, GasSpeed } from '@/__swaps__/types/gas';
import { ParsedAsset } from '@/__swaps__/types/assets';
import { ETH_COLOR, ETH_COLOR_DARK, THICK_BORDER_WIDTH } from '../constants';

const { GasSpeedOrder, CUSTOM, GAS_ICONS, GAS_EMOJIS, getGasLabel, getGasFallback } = gasUtils;
const mockedGasLimit = '21000';

export const GasButton = ({ accentColor }: { accentColor?: string }) => {
export const GasButton = ({ accentColor, isReviewing = false }: { accentColor?: string; isReviewing?: boolean }) => {
const { isDarkMode } = useColorMode();
const { params } = useRoute();
const { currentNetwork } = (params as any) || {};
const chainId = getNetworkObj(currentNetwork).id;
const { selectedGas } = useGasStore();
const { data, isLoading } = useMeteorology({ chainId });
const [nativeAsset, setNativeAsset] = useState<ParsedAddressAsset | undefined>();
const { nativeCurrency } = useAccountSettings();

const separatatorSecondary = useForegroundColor('separatorSecondary');

useEffect(() => {
const getNativeAsset = async () => {
const theNativeAsset = await ethereumUtils.getNativeAssetForNetwork(currentNetwork);
Expand All @@ -41,7 +47,7 @@ export const GasButton = ({ accentColor }: { accentColor?: string }) => {
getNativeAsset();
}, [currentNetwork, setNativeAsset]);

let gasFeeBySpeed: GasFeeParamsBySpeed | GasFeeLegacyParamsBySpeed | any = useMemo(() => {
const gasFeeBySpeed: GasFeeParamsBySpeed | GasFeeLegacyParamsBySpeed | any = useMemo(() => {
if (!isLoading) {
return parseGasFeeParamsBySpeed({
chainId,
Expand All @@ -55,10 +61,76 @@ export const GasButton = ({ accentColor }: { accentColor?: string }) => {
}, [isLoading, nativeAsset]);
const gasFallback = getGasFallback(nativeCurrency);
const [showGasOptions, setShowGasOptions] = useState(false);
// TODO: Move this navigation state inside of SwapNavigation
const [showCustomGasSheet, setShowCustomGasSheet] = useState(false);
const animatedGas = useDerivedValue(() => {
return gasFeeBySpeed[selectedGas?.option]?.gasFee?.display ?? gasFallback;
}, [gasFeeBySpeed, selectedGas]);

const buttonWrapperStyles = useAnimatedStyle(() => {
return {
display: 'flex',
flexDirection: 'row',
backgroundColor: 'transparent',
borderWidth: 2,
borderColor: isDarkMode ? ETH_COLOR_DARK : ETH_COLOR,
borderRadius: 15,
paddingHorizontal: 10,
paddingVertical: 6,
gap: 5,
alignItems: 'center',
justifyContent: 'center',
};
});

if (isReviewing) {
return (
<Inline alignVertical="center" wrap={false}>
<GasMenu gasFeeBySpeed={gasFeeBySpeed} flashbotTransaction={false}>
<ButtonPressAnimation onPress={() => setShowGasOptions(!showGasOptions)}>
<Box as={Animated.View} style={buttonWrapperStyles}>
<Inline alignVertical="center" space="4px">
<TextIcon
color={accentColor ? { custom: accentColor } : 'red'}
height={10}
size="icon 12px"
textStyle={{ marginTop: -1.5 }}
width={16}
weight="bold"
>
􀙭
</TextIcon>
<Text color="label" size="15pt" weight="heavy">
{getGasLabel(selectedGas?.option || GasSpeed.FAST)}
</Text>
</Inline>
<TextIcon color="labelSecondary" height={10} size="icon 13px" weight="bold" width={12}>
􀆏
</TextIcon>
</Box>
</ButtonPressAnimation>
</GasMenu>

<ButtonPressAnimation onPress={() => setShowCustomGasSheet(prev => !prev)}>
<Box
style={{
paddingHorizontal: 7,
paddingVertical: 6,
gap: 10,
borderRadius: 15,
borderWidth: THICK_BORDER_WIDTH,
borderColor: separatatorSecondary,
}}
>
<Text weight="heavy" size="15pt" color="label">
􀌆
</Text>
</Box>
</ButtonPressAnimation>
</Inline>
);
}

return (
<ButtonPressAnimation onPress={() => setShowGasOptions(!showGasOptions)}>
<GasMenu gasFeeBySpeed={gasFeeBySpeed} flashbotTransaction={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Animated, { runOnJS, useAnimatedGestureHandler } from 'react-native-reani
import { ButtonPressAnimation } from '@/components/animations';
import { IS_IOS } from '@/env';

type GestureHandlerButtonProps = {
export type GestureHandlerButtonProps = {
children: React.ReactNode;
disableButtonPressWrapper?: boolean;
disabled?: boolean;
Expand Down