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

Swaps: Input List #5728

Closed
wants to merge 17 commits into from
19 changes: 18 additions & 1 deletion src/__swaps__/screens/Swap/Swap.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import Animated from 'react-native-reanimated';
import { ScreenCornerRadius } from 'react-native-screen-corner-radius';
Expand All @@ -21,6 +21,8 @@ import { SwapBottomPanel } from '@/__swaps__/screens/Swap/components/SwapBottomP
import { SwapWarning } from './components/SwapWarning';
import { useSwapContext } from './providers/swap-provider';
import { UserAssetsSync } from './components/UserAssetsSync';
import { swapsStore } from '@/state/swaps/swapsStore';
import { ChainId } from '@/__swaps__/types/chains';

/** README
* This prototype is largely driven by Reanimated and Gesture Handler, which
Expand Down Expand Up @@ -61,6 +63,21 @@ import { UserAssetsSync } from './components/UserAssetsSync';

export function SwapScreen() {
const { AnimatedSwapStyles } = useSwapContext();

useEffect(() => {
return () => {
swapsStore.setState({
filter: 'all',
inputSearchQuery: '',
outputSearchQuery: '',
selectedOutputChainId: ChainId.mainnet,
quote: null,
inputAsset: null,
outputAsset: null,
});
};
}, []);

return (
<SwapSheetGestureBlocker>
<Box as={Page} style={styles.rootViewBackground} testID="swap-screen" width="full">
Expand Down
174 changes: 174 additions & 0 deletions src/__swaps__/screens/Swap/components/CoinRow2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React, { useCallback, useMemo } from 'react';
import { ButtonPressAnimation } from '@/components/animations';
import { Box, HitSlop, Inline, Stack, Text } from '@/design-system';
import { TextColor } from '@/design-system/color/palettes';
import { CoinRowButton } from '@/__swaps__/screens/Swap/components/CoinRowButton';
import { BalancePill } from '@/__swaps__/screens/Swap/components/BalancePill';
import { StyleSheet } from 'react-native';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { UniqueId } from '@/__swaps__/types/assets';
import { userAssetsStore } from '@/state/assets/userAssets';
import { parseSearchAsset } from '@/__swaps__/utils/assets';
import { SwapAssetType } from '@/__swaps__/types/swap';
import { toggleFavorite } from '@/resources/favorites';
import { SwapCoinIcon } from './SwapCoinIcon';
import { ethereumUtils } from '@/utils';

const CoinName = ({ assetId }: { assetId: UniqueId }) => {
const name = userAssetsStore(state => state.getUserAsset(assetId).name);
return (
<Text color="label" size="17pt" weight="semibold">
{name}
</Text>
);
};

const CoinUserBalance = ({ assetId }: { assetId: UniqueId }) => {
const balance = userAssetsStore(state => state.getUserAsset(assetId).balance.display);
return (
<Text color="labelTertiary" size="13pt" weight="semibold">
{balance}
</Text>
);
};

const CoinSymbol = ({ assetId }: { assetId: UniqueId }) => {
const symbol = userAssetsStore(state => state.getUserAsset(assetId).symbol);
return (
<Text color="labelTertiary" size="13pt" weight="semibold">
{symbol}
</Text>
);
};

const CoinPercentChange = ({ assetId }: { assetId: UniqueId }) => {
const isTrending = false; // fix this when implementing token to sell list

const percentChange = useMemo(() => {
if (isTrending) {
const rawChange = Math.random() * 30;
const isNegative = Math.random() < 0.2;
const prefix = isNegative ? '-' : '+';
const color: TextColor = isNegative ? 'red' : 'green';
const change = `${rawChange.toFixed(1)}%`;

return { change, color, prefix };
}
}, [isTrending]);

if (!isTrending || !percentChange) return null;

return (
<Inline alignVertical="center" space={{ custom: 1 }}>
<Text align="center" color={percentChange.color} size="12pt" weight="bold">
{percentChange.prefix}
</Text>
<Text color={percentChange.color} size="13pt" weight="semibold">
{percentChange.change}
</Text>
</Inline>
);
};

const CoinIcon = ({ assetId }: { assetId: UniqueId }) => {
const { symbol, iconUrl, address, mainnetAddress, chainId, color } = userAssetsStore(state => {
const asset = state.getUserAsset(assetId);
return {
symbol: asset.symbol,
iconUrl: asset.icon_url,
address: asset.address,
mainnetAddress: asset.mainnetAddress,
chainId: asset.chainId,
color: asset.colors?.primary ?? asset.colors?.fallback,
};
});
return (
<SwapCoinIcon
iconUrl={iconUrl}
address={address}
mainnetAddress={mainnetAddress}
large
network={ethereumUtils.getNetworkFromChainId(chainId)}
symbol={symbol}
color={color}
/>
);
};

const CoinInfoButton = ({ assetId }: { assetId: UniqueId }) => {
return <CoinRowButton icon="􀅳" outline size="icon 14px" />;
};

const CoinFavoriteButton = ({ assetId, isFavorited }: { assetId: UniqueId; isFavorited: boolean }) => {
const address = userAssetsStore(state => state.getUserAsset(assetId).address);
return <CoinRowButton color={isFavorited ? '#FFCB0F' : undefined} onPress={() => toggleFavorite(address)} icon="􀋃" weight="black" />;
};

const CoinActions = ({ assetId, isFavorited }: { assetId: UniqueId; isFavorited: boolean }) => {
return (
<Inline space="8px">
<CoinInfoButton assetId={assetId} />
<CoinFavoriteButton assetId={assetId} isFavorited={isFavorited} />
</Inline>
);
};

const CoinBalance = ({ assetId }: { assetId: UniqueId }) => {
const nativeBalance = userAssetsStore(state => state.getUserAsset(assetId).native.balance.display);
return <BalancePill balance={nativeBalance} />;
};

export const CoinRow2 = React.memo(
({ assetId, isFavorited = false, output = false }: { assetId: string; isFavorited?: boolean; output?: boolean }) => {
const { setAsset } = useSwapContext();

const handleSelectToken = useCallback(() => {
const userAsset = userAssetsStore.getState().getUserAsset(assetId);
const parsedAsset = parseSearchAsset({
assetWithPrice: undefined,
searchAsset: userAsset,
userAsset,
});

setAsset({
type: SwapAssetType.inputAsset,
asset: parsedAsset,
});
}, [assetId, setAsset]);

return (
<ButtonPressAnimation disallowInterruption onPress={handleSelectToken} scaleTo={0.95}>
<HitSlop vertical="10px">
<Box
alignItems="center"
paddingVertical="10px"
paddingHorizontal="20px"
flexDirection="row"
justifyContent="space-between"
width="full"
>
<Inline alignVertical="center" space="10px">
<CoinIcon assetId={assetId} />
<Stack space="10px">
<CoinName assetId={assetId} />
<Inline alignVertical="center" space={{ custom: 5 }}>
{!output ? <CoinUserBalance assetId={assetId} /> : <CoinSymbol assetId={assetId} />}
<CoinPercentChange assetId={assetId} />
</Inline>
</Stack>
</Inline>
{output ? <CoinActions assetId={assetId} isFavorited={isFavorited} /> : <CoinBalance assetId={assetId} />}
</Box>
</HitSlop>
</ButtonPressAnimation>
);
}
);

CoinRow2.displayName = 'CoinRow';

export const styles = StyleSheet.create({
solidColorCoinIcon: {
opacity: 0.4,
},
});
18 changes: 11 additions & 7 deletions src/__swaps__/screens/Swap/components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { AnimatedText, Bleed, Box, Column, Columns, Text, useColorMode, useForeg
import { LIGHT_SEPARATOR_COLOR, SEPARATOR_COLOR, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { getColorValueForThemeWorklet, opacity } from '@/__swaps__/utils/swaps';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { userAssetsStore } from '@/state/assets/userAssets';
import { ExtendedAnimatedAssetWithColors } from '@/__swaps__/types/assets';
import { swapsStore } from '@/state/swaps/swapsStore';

const AnimatedInput = Animated.createAnimatedComponent(Input);

Expand Down Expand Up @@ -39,12 +39,17 @@ export const SearchInput = ({
});

const defaultValue = useMemo(() => {
return userAssetsStore.getState().searchQuery;
}, []);
return output ? swapsStore.getState().outputSearchQuery : swapsStore.getState().inputSearchQuery;
}, [output]);

const onSearchQueryChange = useCallback((event: NativeSyntheticEvent<TextInputChangeEventData>) => {
userAssetsStore.setState({ searchQuery: event.nativeEvent.text });
}, []);
const onSearchQueryChange = useCallback(
(event: NativeSyntheticEvent<TextInputChangeEventData>) => {
output
? swapsStore.setState({ outputSearchQuery: event.nativeEvent.text })
: swapsStore.setState({ inputSearchQuery: event.nativeEvent.text });
},
[output]
);

const searchInputValue = useAnimatedProps(() => {
const isFocused = inputProgress.value >= 1 || outputProgress.value >= 1;
Expand Down Expand Up @@ -88,7 +93,6 @@ export const SearchInput = ({
animatedProps={searchInputValue}
onChange={onSearchQueryChange}
onBlur={() => {
console.log('here');
onSearchQueryChange({
nativeEvent: {
text: '',
Expand Down
18 changes: 3 additions & 15 deletions src/__swaps__/screens/Swap/components/SwapInputAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenLi
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { IS_ANDROID } from '@/env';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { useAssetsToSell } from '@/__swaps__/screens/Swap/hooks/useAssetsToSell';
import { isSameAssetWorklet } from '@/__swaps__/utils/assets';
import { AmimatedSwapCoinIcon } from './AnimatedSwapCoinIcon';
import { swapsStore } from '@/state/swaps/swapsStore';

function SwapInputActionButton() {
const { isDarkMode } = useColorMode();
Expand Down Expand Up @@ -80,21 +79,10 @@ function SwapInputIcon() {
}

function InputAssetBalanceBadge() {
const { internalSelectedInputAsset } = useSwapContext();

const userAssets = useAssetsToSell();
const selectedAssetBalance = swapsStore(state => state.inputAsset?.balance.display);

const label = useDerivedValue(() => {
const asset = internalSelectedInputAsset.value;
if (!asset) return 'No balance';

const userAsset = userAssets.find(userAsset =>
isSameAssetWorklet(userAsset, {
address: asset.address,
chainId: asset.chainId,
})
);
return userAsset?.balance.display ?? 'No balance';
return selectedAssetBalance || 'No Balance';
});

return <BalanceBadge label={label} />;
Expand Down
22 changes: 7 additions & 15 deletions src/__swaps__/screens/Swap/components/SwapOutputAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { TokenList } from '@/__swaps__/screens/Swap/components/TokenList/TokenLi
import { BASE_INPUT_WIDTH, INPUT_INNER_WIDTH, INPUT_PADDING, THICK_BORDER_WIDTH } from '@/__swaps__/screens/Swap/constants';
import { IS_ANDROID } from '@/env';
import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider';
import { isSameAssetWorklet } from '@/__swaps__/utils/assets';
import { useAssetsToSell } from '@/__swaps__/screens/Swap/hooks/useAssetsToSell';
import { AmimatedSwapCoinIcon } from './AnimatedSwapCoinIcon';
import { swapsStore } from '@/state/swaps/swapsStore';
import { userAssetsStore } from '@/state/assets/userAssets';

function SwapOutputActionButton() {
const { isDarkMode } = useColorMode();
Expand Down Expand Up @@ -80,21 +80,13 @@ function SwapInputIcon() {
}

function OutputAssetBalanceBadge() {
const { internalSelectedOutputAsset } = useSwapContext();

const userAssets = useAssetsToSell();
const selectedAssetId = swapsStore(state => state.outputAsset?.uniqueId);
const selectedAssetBalance = userAssetsStore(state =>
selectedAssetId && state.hasUserAsset(selectedAssetId) ? state.getUserAsset(selectedAssetId).balance.display : undefined
);

const label = useDerivedValue(() => {
const asset = internalSelectedOutputAsset.value;
if (!asset) return 'No balance';

const userAsset = userAssets.find(userAsset =>
isSameAssetWorklet(userAsset, {
address: asset.address,
chainId: asset.chainId,
})
);
return userAsset?.balance.display ?? 'No balance';
return selectedAssetBalance || 'No Balance';
});

return <BalanceBadge label={label} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { useSwapContext } from '@/__swaps__/screens/Swap/providers/swap-provider
import { ContextMenuButton } from '@/components/context-menu';
import { useAccountAccentColor } from '@/hooks';
import { OnPressMenuItemEventObject } from 'react-native-ios-context-menu';
import { userAssetsStore } from '@/state/assets/userAssets';
import { swapsStore } from '@/state/swaps/swapsStore';

type ChainSelectionProps = {
allText?: string;
Expand All @@ -35,7 +35,7 @@ export const ChainSelection = ({ allText, output }: ChainSelectionProps) => {
const red = useForegroundColor('red');

const initialFilter = useMemo(() => {
return userAssetsStore.getState().filter;
return swapsStore.getState().filter;
}, []);

const accentColor = useMemo(() => {
Expand Down Expand Up @@ -71,7 +71,7 @@ export const ChainSelection = ({ allText, output }: ChainSelectionProps) => {
if (output) {
setSelectedOutputChainId(Number(actionKey) as ChainId);
} else {
userAssetsStore.setState({
swapsStore.setState({
filter: actionKey === 'all' ? 'all' : (Number(actionKey) as ChainId),
});
runOnUI(() => {
Expand Down
Loading
Loading