Repository: trezor/trezor-suite
Pull request #18977: Mobile trade visual fixes
Author: login: jbazant
Labels: name: mobile
description: Suite Lite issues and PRs
name: release-native
description: Has to be included in the upcoming release.
name: no-project
description: This label is used to specify that PR doesn't need to be added to a project
Created: 2025-05-15T15:58:32Z | Merged: 2025-05-16T07:33:31Z

<!--- Provide a general summary of your changes in the Title above -->

## Description
- Fixed bug that provider was sometimes not visually selected in provider sheet.
- Changed "Favourites" and "All assets" animation from FadeInUp to FadeIn.
- When buy form is displayed immediately after navigation there are no animations (animations after skeleton are still present).
- Fixed animations on footer (was always FadeInDown).
- Buy header does never FadeIn (FadeInUp still present as before).
- Buy header is now positioned same way as e.g. Preferences header.
- Skeleton visual updated (again).
- Android only changes:
  - No ugly shadow fade animations.
  - Shadow removed for country list to allow FadeIn of items.
  - Animations changed for Cards on buy form screen (to allow to still keep shadows).

<!--- Describe your changes in detail -->

## Related Issue

Resolve <!--- link the issue here -->

## Screenshots:

### Android before changes
https://github.com/user-attachments/assets/aee64c1c-2367-4f25-9e3a-dcd22c7b95c7

### Android after changes
https://github.com/user-attachments/assets/0d20cee9-6bb2-4495-879f-a2b40adbaf36

### iOS after changes
https://github.com/user-attachments/assets/30b9c5c8-44ec-40af-91bd-eb0ae2a05ba8




🔍🖥️ **Suite native android test results:** [View in Currents](https://app.currents.dev/projects/iUe1Y4/runs?branch=mobile-trade-visual-fixes&lookback=7)


Diff:
diff --git a/suite-native/atoms/src/Card/Card.tsx b/suite-native/atoms/src/Card/Card.tsx
index 1eb3d3472461..20fef0e237d1 100644
--- a/suite-native/atoms/src/Card/Card.tsx
+++ b/suite-native/atoms/src/Card/Card.tsx
@@ -15,6 +15,7 @@ export type CardProps = {
     children: ReactNode;
     style?: NativeStyleObject;
     noPadding?: boolean;
+    noShadow?: boolean;
     borderColor?: Color;
     alertProps?: InlineAlertBoxProps;
     alertPosition?: AlertPosition;
@@ -30,11 +31,11 @@ const cardInnerContainerStyle = prepareNativeStyle<{
     alertPosition?: AlertPosition;
     noPadding: boolean;
     borderColor?: Color;
-}>((utils, { alertPosition, noPadding, borderColor }) => ({
+    noShadow?: boolean;
+}>((utils, { alertPosition, noPadding, borderColor, noShadow }) => ({
     backgroundColor: utils.colors.backgroundSurfaceElevation1,
     borderRadius: utils.borders.radii.r16,
     padding: noPadding ? 0 : utils.spacings.sp16,
-    ...utils.boxShadows.small,
 
     extend: [
         {
@@ -58,6 +59,10 @@ const cardInnerContainerStyle = prepareNativeStyle<{
                 borderWidth: utils.borders.widths.small,
             },
         },
+        {
+            condition: !noShadow,
+            style: utils.boxShadows.small,
+        },
     ],
 }));
 
@@ -88,7 +93,15 @@ const alertBoxWrapperStyle = prepareNativeStyle<{
 
 export const Card = React.forwardRef<View, CardProps>(
     (
-        { children, style, alertProps, alertPosition, borderColor, noPadding = false }: CardProps,
+        {
+            children,
+            style,
+            alertProps,
+            alertPosition,
+            borderColor,
+            noPadding = false,
+            noShadow = false,
+        }: CardProps,
         ref,
     ) => {
         const { applyStyle } = useNativeStyles();
@@ -107,6 +120,7 @@ export const Card = React.forwardRef<View, CardProps>(
                             alertPosition,
                             noPadding,
                             borderColor,
+                            noShadow,
                         }),
                         style,
                     ]}
diff --git a/suite-native/module-trading/src/components/buy/BuyCard.tsx b/suite-native/module-trading/src/components/buy/BuyCard.tsx
index dc931e4da5b1..8f255575120e 100644
--- a/suite-native/module-trading/src/components/buy/BuyCard.tsx
+++ b/suite-native/module-trading/src/components/buy/BuyCard.tsx
@@ -1,6 +1,6 @@
+import { Platform } from 'react-native';
 import {
     FadeIn,
-    FadeOut,
     LinearTransition,
     interpolateColor,
     useAnimatedStyle,
@@ -23,6 +23,7 @@ import { useTradingBuyFormContext } from '../../hooks/useTradingBuyFormContext';
 
 type BuyCardProps = {
     isAmountInputActive: boolean;
+    shouldAnimateEntering?: boolean;
 };
 
 const BUY_CARD_TEST_ID = '@trading/buyCard';
@@ -52,15 +53,18 @@ const useAnimatedBorderStyle = (isAmountInputActive: boolean) => {
     }));
 };
 
-export const BuyCard = ({ isAmountInputActive }: BuyCardProps) => {
+export const BuyCard = ({ isAmountInputActive, shouldAnimateEntering }: BuyCardProps) => {
     const { applyStyle } = useNativeStyles();
     const animatedStyle = useAnimatedBorderStyle(isAmountInputActive);
     const { watch } = useTradingBuyFormContext();
 
     const asset = watch('asset');
 
+    // on android fade animation looks ugly on view with shadows, better to skip it
+    const enteringAnimation = shouldAnimateEntering && Platform.OS === 'ios' ? FadeIn : undefined;
+
     return (
-        <AnimatedBox entering={FadeIn} exiting={FadeOut} layout={LinearTransition}>
+        <AnimatedBox entering={enteringAnimation} layout={LinearTransition}>
             <AnimatedCard style={animatedStyle} noPadding>
                 <VStack
                     style={applyStyle(buySectionStyle, { bottomBorder: true })}
diff --git a/suite-native/module-trading/src/components/buy/BuyForm.tsx b/suite-native/module-trading/src/components/buy/BuyForm.tsx
index 27a2a4711cc7..b1423aff9266 100644
--- a/suite-native/module-trading/src/components/buy/BuyForm.tsx
+++ b/suite-native/module-trading/src/components/buy/BuyForm.tsx
@@ -1,4 +1,5 @@
 import { memo } from 'react';
+import { FadeIn, FadeInDown, FadeOutDown } from 'react-native-reanimated';
 
 import { VStack } from '@suite-native/atoms';
 import { useDebouncedValue } from '@trezor/react-utils';
@@ -15,41 +16,82 @@ import { TradeHistoryButton } from '../general/TradeHistory/TradeHistoryButton';
 import { TradingAlert } from '../general/TradingAlert';
 import { TradingFooter } from '../general/TradingFooter';
 
+type BuyFormProps = {
+    shouldAnimateEntering?: boolean;
+};
+
+type BuyFormMemoizedProps = {
+    isAmountInputActive: boolean;
+    shouldAnimateEntering?: boolean;
+};
+
 const BUY_FORM_TEST_ID = '@trading/buy/form';
 
-const BuyFormMemoized = memo(({ isAmountInputActive }: { isAmountInputActive: boolean }) => {
-    // `isFormMountedRecently` allows to use different animations for initial form load (FadeIn)
-    // and when user interacts with the form (FadeInUp/FadeInDown)
-    const isFormMountedRecently = useMountedRecentlyFlag();
+const getEnteringAnimationForItemsUnderBuyCard = (
+    isFormMountedRecently?: boolean,
+    shouldAnimateEntering?: boolean,
+) => {
+    if (!isFormMountedRecently) {
+        return FadeInDown;
+    }
 
-    return (
-        <VStack spacing="sp16" testID={BUY_FORM_TEST_ID}>
-            {!isAmountInputActive && <BuyHeader isFormMountedRecently={isFormMountedRecently} />}
-            <TradingAlert />
-            <BuyCard isAmountInputActive={isAmountInputActive} />
-            {isAmountInputActive ? (
-                <AmountEditingDoneButton />
-            ) : (
-                <>
-                    <PaymentCard isFormMountedRecently={isFormMountedRecently} />
-                    <Confirmation isFormMountedRecently={isFormMountedRecently} />
-                    <TradingFooter isFormMountedRecently={isFormMountedRecently} />
-                    <TradeHistoryButton
-                        tradeType="buy"
-                        isFormMountedRecently={isFormMountedRecently}
-                    />
-                </>
-            )}
-        </VStack>
-    );
-});
+    return shouldAnimateEntering ? FadeIn : undefined;
+};
+
+const BuyFormMemoized = memo(
+    ({ isAmountInputActive, shouldAnimateEntering }: BuyFormMemoizedProps) => {
+        // `isFormMountedRecently` allows to use different animations for initial form load (FadeIn)
+        // and when user interacts with the form (FadeInUp/FadeInDown)
+        const isFormMountedRecently = useMountedRecentlyFlag();
+
+        const enteringAnimation = getEnteringAnimationForItemsUnderBuyCard(
+            isFormMountedRecently,
+            shouldAnimateEntering,
+        );
 
-export const BuyForm = () => {
+        return (
+            <VStack spacing="sp16" paddingTop="sp16" testID={BUY_FORM_TEST_ID}>
+                {!isAmountInputActive && (
+                    <BuyHeader isFormMountedRecently={isFormMountedRecently} />
+                )}
+                <TradingAlert />
+                <BuyCard
+                    isAmountInputActive={isAmountInputActive}
+                    shouldAnimateEntering={shouldAnimateEntering}
+                />
+                {isAmountInputActive ? (
+                    <AmountEditingDoneButton />
+                ) : (
+                    <>
+                        <PaymentCard
+                            isFormMountedRecently={isFormMountedRecently}
+                            shouldAnimateEntering={shouldAnimateEntering}
+                        />
+                        <Confirmation enteringAnimation={enteringAnimation} />
+                        <TradingFooter enteringAnimation={enteringAnimation} />
+                        <TradeHistoryButton
+                            tradeType="buy"
+                            enteringAnimation={enteringAnimation}
+                            exitingAnimation={FadeOutDown}
+                        />
+                    </>
+                )}
+            </VStack>
+        );
+    },
+);
+
+export const BuyForm = ({ shouldAnimateEntering }: BuyFormProps) => {
     const buyForm = useTradingBuyFormContext();
     useBuyQuotes(buyForm);
 
     const isAmountInputActive = !!buyForm.watch('focusedValue');
     const isAmountInputActiveDebounced = useDebouncedValue(isAmountInputActive);
 
-    return <BuyFormMemoized isAmountInputActive={isAmountInputActiveDebounced} />;
+    return (
+        <BuyFormMemoized
+            isAmountInputActive={isAmountInputActiveDebounced}
+            shouldAnimateEntering={shouldAnimateEntering}
+        />
+    );
 };
diff --git a/suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx b/suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx
index 19dc7a553ab5..4923b269a5fd 100644
--- a/suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx
+++ b/suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx
@@ -1,50 +1,58 @@
 import { Dimensions } from 'react-native';
 
-import { BoxSkeleton, Card, HStack, Text, VStack } from '@suite-native/atoms';
-import { Translation } from '@suite-native/intl';
+import { BoxSkeleton, Card, HStack, VStack } from '@suite-native/atoms';
 
+import { BuyHeader } from './BuyHeader';
 import { TradingFooter } from '../general/TradingFooter';
 
-const SKELETON_LARGE_WIDTH = Dimensions.get('window').width * 0.3;
 const SKELETON_LARGE_HEIGHT = 60;
-const SKELETON_SMALL_WIDTH = Dimensions.get('window').width * 0.2;
 const SKELETON_SMALL_HEIGHT = 20;
 
-const SkeletonSmall = () => (
-    <BoxSkeleton width={SKELETON_SMALL_WIDTH} height={SKELETON_SMALL_HEIGHT} />
+type SkeletonProps = {
+    widthPercentage: number;
+};
+
+type SkeletonRowProps = {
+    leftWidthPercentage: number;
+    rightWidthPercentage: number;
+};
+
+const SkeletonSmall = ({ widthPercentage }: SkeletonProps) => (
+    <BoxSkeleton
+        width={Dimensions.get('window').width * widthPercentage}
+        height={SKELETON_SMALL_HEIGHT}
+    />
+);
+
+const SkeletonLarge = ({ widthPercentage }: SkeletonProps) => (
+    <BoxSkeleton
+        width={Dimensions.get('window').width * widthPercentage}
+        height={SKELETON_LARGE_HEIGHT}
+        borderRadius="r16"
+    />
 );
 
-const SkeletonLarge = () => (
-    <BoxSkeleton width={SKELETON_LARGE_WIDTH} height={SKELETON_LARGE_HEIGHT} borderRadius="r16" />
+const SkeletonLargeRow = ({ leftWidthPercentage, rightWidthPercentage }: SkeletonRowProps) => (
+    <HStack justifyContent="space-between" alignItems="center">
+        <SkeletonLarge widthPercentage={leftWidthPercentage} />
+        <SkeletonLarge widthPercentage={rightWidthPercentage} />
+    </HStack>
 );
 
 export const BuyFormSkeleton = () => (
-    <VStack spacing="sp16">
-        <Text variant="titleSmall" color="textDefault">
-            <Translation id="moduleTrading.tradingScreen.buyTitle" />
-        </Text>
-        <VStack spacing="sp16">
-            <Card>
-                <VStack>
-                    <SkeletonSmall />
-                    <HStack justifyContent="space-between" alignItems="center">
-                        <SkeletonLarge />
-                        <SkeletonLarge />
-                    </HStack>
-                    <SkeletonSmall />
-                    <HStack justifyContent="space-between" alignItems="center">
-                        <SkeletonLarge />
-                        <SkeletonLarge />
-                    </HStack>
-                </VStack>
-            </Card>
-            <Card>
-                <HStack justifyContent="space-between" alignItems="center">
-                    <SkeletonLarge />
-                    <SkeletonLarge />
-                </HStack>
-            </Card>
-        </VStack>
-        <TradingFooter isFormMountedRecently={true} />
+    <VStack spacing="sp16" paddingTop="sp16">
+        <BuyHeader isFormMountedRecently={true} />
+        <Card>
+            <VStack>
+                <SkeletonSmall widthPercentage={0.2} />
+                <SkeletonLargeRow leftWidthPercentage={0.3} rightWidthPercentage={0.35} />
+                <SkeletonSmall widthPercentage={0.25} />
+                <SkeletonLargeRow leftWidthPercentage={0.4} rightWidthPercentage={0.3} />
+            </VStack>
+        </Card>
+        <Card>
+            <SkeletonLargeRow leftWidthPercentage={0.35} rightWidthPercentage={0.35} />
+        </Card>
+        <TradingFooter />
     </VStack>
 );
diff --git a/suite-native/module-trading/src/components/buy/BuyHeader.tsx b/suite-native/module-trading/src/components/buy/BuyHeader.tsx
index 13cac4f5bda7..00a04565f734 100644
--- a/suite-native/module-trading/src/components/buy/BuyHeader.tsx
+++ b/suite-native/module-trading/src/components/buy/BuyHeader.tsx
@@ -1,4 +1,4 @@
-import { FadeIn, FadeInUp, FadeOutUp } from 'react-native-reanimated';
+import { FadeInUp, FadeOutUp } from 'react-native-reanimated';
 
 import { AnimatedBox, Text } from '@suite-native/atoms';
 import { Translation } from '@suite-native/intl';
@@ -8,7 +8,11 @@ export type BuyHeaderProps = {
 };
 
 export const BuyHeader = ({ isFormMountedRecently }: BuyHeaderProps) => (
-    <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInUp} exiting={FadeOutUp}>
+    <AnimatedBox
+        entering={isFormMountedRecently ? undefined : FadeInUp}
+        exiting={FadeOutUp}
+        paddingHorizontal="sp16"
+    >
         <Text variant="titleSmall" color="textDefault">
             <Translation id="moduleTrading.tradingScreen.buyTitle" />
         </Text>
diff --git a/suite-native/module-trading/src/components/buy/Confirmation.tsx b/suite-native/module-trading/src/components/buy/Confirmation.tsx
index 2340446a91e2..326334d276b1 100644
--- a/suite-native/module-trading/src/components/buy/Confirmation.tsx
+++ b/suite-native/module-trading/src/components/buy/Confirmation.tsx
@@ -1,4 +1,4 @@
-import { FadeIn, FadeInDown, FadeOutDown } from 'react-native-reanimated';
+import { AnimatedProps, FadeIn, FadeOutDown } from 'react-native-reanimated';
 
 import { AnimatedBox, Button } from '@suite-native/atoms';
 import { Translation } from '@suite-native/intl';
@@ -8,12 +8,12 @@ import { useTradingBuyFlow } from '../../hooks/useTradingBuyFlow';
 import { useTradingBuyFormContext } from '../../hooks/useTradingBuyFormContext';
 
 export type ConfirmationProps = {
-    isFormMountedRecently?: boolean;
+    enteringAnimation?: AnimatedProps<any>['entering'];
 };
 
 const CONFIRMATION_TEST_ID = '@trading/buy/continue-button';
 
-export const Confirmation = ({ isFormMountedRecently }: ConfirmationProps) => {
+export const Confirmation = ({ enteringAnimation }: ConfirmationProps) => {
     const form = useTradingBuyFormContext();
 
     const { canProceed, selectQuote, isConsentRequested, giveConsent, cancelConsent } =
@@ -22,7 +22,7 @@ export const Confirmation = ({ isFormMountedRecently }: ConfirmationProps) => {
     const quote = form.watch('quote');
 
     return (
-        <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
+        <AnimatedBox entering={enteringAnimation} exiting={FadeOutDown}>
             {canProceed && (
                 <AnimatedBox entering={FadeIn}>
                     <Button onPress={selectQuote} testID={CONFIRMATION_TEST_ID}>
diff --git a/suite-native/module-trading/src/components/buy/PaymentCard.tsx b/suite-native/module-trading/src/components/buy/PaymentCard.tsx
index e7db53efad39..8458e2ae41c4 100644
--- a/suite-native/module-trading/src/components/buy/PaymentCard.tsx
+++ b/suite-native/module-trading/src/components/buy/PaymentCard.tsx
@@ -1,4 +1,5 @@
-import { FadeIn, FadeInDown, FadeOutDown } from 'react-native-reanimated';
+import { Platform } from 'react-native';
+import { FadeIn, FadeInDown, FadeOutUp, StretchInY, StretchOutY } from 'react-native-reanimated';
 
 import { AnimatedBox, Card } from '@suite-native/atoms';
 
@@ -8,14 +9,37 @@ import { TradingProviderPicker } from './TradingProviderPicker';
 
 export type PaymentCardProps = {
     isFormMountedRecently?: boolean;
+    shouldAnimateEntering?: boolean;
 };
 
-export const PaymentCard = ({ isFormMountedRecently }: PaymentCardProps) => (
-    <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
-        <Card noPadding>
-            <PaymentMethodPicker />
-            <CountryOfResidencePicker />
-            <TradingProviderPicker />
-        </Card>
-    </AnimatedBox>
-);
+const getEnteringAnimation = (isFormMountedRecently?: boolean, shouldAnimateEntering?: boolean) => {
+    // on android fade animation looks ugly on view with shadows, better to skip the initial one
+    // and use stretch animation instead for the rest of the time
+    if (Platform.OS === 'android') {
+        return isFormMountedRecently ? undefined : StretchInY;
+    }
+
+    if (isFormMountedRecently) {
+        return shouldAnimateEntering ? FadeIn : undefined;
+    }
+
+    return FadeInDown;
+};
+
+// on android fade animation looks ugly on view with shadows, better to use stretch one here
+const getExitingAnimation = () => (Platform.OS === 'android' ? StretchOutY : FadeOutUp);
+
+export const PaymentCard = ({ isFormMountedRecently, shouldAnimateEntering }: PaymentCardProps) => {
+    const enteringAnimation = getEnteringAnimation(isFormMountedRecently, shouldAnimateEntering);
+    const exitingAnimation = getExitingAnimation();
+
+    return (
+        <AnimatedBox entering={enteringAnimation} exiting={exitingAnimation}>
+            <Card noPadding>
+                <PaymentMethodPicker />
+                <CountryOfResidencePicker />
+                <TradingProviderPicker />
+            </Card>
+        </AnimatedBox>
+    );
+};
diff --git a/suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx b/suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx
index 4a5644ba04c5..f66a652f39f4 100644
--- a/suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx
+++ b/suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx
@@ -1,5 +1,5 @@
 import { ReactNode } from 'react';
-import { Pressable } from 'react-native';
+import { Platform, Pressable } from 'react-native';
 import { FadeIn, FadeOut } from 'react-native-reanimated';
 
 import { AnimatedBox, Card, HStack, Radio, Text } from '@suite-native/atoms';
@@ -20,11 +20,14 @@ const wrapperStyle = prepareNativeStyle(({ spacings }) => ({
 
 export const CountryListItem = ({ label, onPress, value, isSelected }: CountryListItemProps) => {
     const { applyStyle } = useNativeStyles();
+    // on android fade animation looks ugly on view with shadows, but without animation it looks even worse
+    // do not display shadow on android and keep animating
+    const noShadow = Platform.OS === 'android';
 
     return (
         <AnimatedBox entering={FadeIn} exiting={FadeOut}>
             <Pressable onPress={onPress} style={applyStyle(wrapperStyle)}>
-                <Card>
+                <Card noShadow={noShadow}>
                     <HStack alignItems="center" justifyContent="space-between">
                         <HStack>
                             <Text variant="body" color="textDefault">
diff --git a/suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx b/suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
index d9b0f330b941..d00570509350 100644
--- a/suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
+++ b/suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
@@ -62,7 +62,7 @@ export const ProvidersSheet = ({
                         companyName={companyName ?? ''}
                         logo={logo ?? ''}
                         onPress={() => onQuoteSelectCallback(item)}
-                        isSelected={item.orderId === selectedQuote?.orderId}
+                        isSelected={item.exchange === selectedQuote?.exchange}
                     />
                 );
             }}
diff --git a/suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx b/suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx
index dbae0972fa9d..302e57f6bc49 100644
--- a/suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx
+++ b/suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx
@@ -1,5 +1,5 @@
 import { Pressable } from 'react-native';
-import { FadeIn, FadeInDown, FadeOutDown } from 'react-native-reanimated';
+import { AnimatedProps } from 'react-native-reanimated';
 import { useSelector } from 'react-redux';
 
 import { useNavigation } from '@react-navigation/native';
@@ -23,7 +23,8 @@ import { prepareNativeStyle, useNativeStyles } from '@trezor/styles/src';
 
 type TradeHistoryButtonProps = {
     tradeType: TradingType;
-    isFormMountedRecently?: boolean;
+    enteringAnimation: AnimatedProps<Record<never, unknown>>['entering'];
+    exitingAnimation: AnimatedProps<Record<never, unknown>>['exiting'];
 };
 
 export type NavigationProps = StackToStackCompositeNavigationProps<
@@ -48,7 +49,8 @@ const buttonStyle = prepareNativeStyle(utils => ({
 
 export const TradeHistoryButton = ({
     tradeType,
-    isFormMountedRecently,
+    enteringAnimation,
+    exitingAnimation,
 }: TradeHistoryButtonProps) => {
     const { applyStyle } = useNativeStyles();
     const navigation = useNavigation<NavigationProps>();
@@ -63,7 +65,7 @@ export const TradeHistoryButton = ({
     const handleOnPress = () => navigation.navigate(TradingStackRoutes.TradeHistory, { tradeType });
 
     return (
-        <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
+        <AnimatedBox entering={enteringAnimation} exiting={exitingAnimation}>
             <Pressable onPress={handleOnPress} testID={TRADE_HISTORY_BUTTON_TEST_ID}>
                 <HStack style={applyStyle(buttonStyle)}>
                     <Text variant="body" color="textSubdued">
diff --git a/suite-native/module-trading/src/components/general/TradingFooter.tsx b/suite-native/module-trading/src/components/general/TradingFooter.tsx
index 427782869924..df571e066cdc 100644
--- a/suite-native/module-trading/src/components/general/TradingFooter.tsx
+++ b/suite-native/module-trading/src/components/general/TradingFooter.tsx
@@ -1,23 +1,24 @@
 import { useMemo } from 'react';
 import { TouchableOpacity } from 'react-native';
-import { FadeIn, FadeInDown, FadeOutDown } from 'react-native-reanimated';
+import { AnimatedProps } from 'react-native-reanimated';
 
 import { AnimatedBox, Button, HStack, Image, Text } from '@suite-native/atoms';
 import { Translation } from '@suite-native/intl';
 import { useOpenLink } from '@suite-native/link';
 
 export type TradingFooterProps = {
-    isFormMountedRecently?: boolean;
+    enteringAnimation?: AnimatedProps<any>['entering'];
+    exitingAnimation?: AnimatedProps<any>['exiting'];
 };
 
-export const TradingFooter = ({ isFormMountedRecently }: TradingFooterProps) => {
+export const TradingFooter = ({ enteringAnimation, exitingAnimation }: TradingFooterProps) => {
     const openLink = useOpenLink();
 
     const imageSource = useMemo(() => require('../../../assets/InvityLogo.png'), []);
     const openLinkToInvity = () => openLink('https://invity.io');
 
     return (
-        <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
+        <AnimatedBox entering={enteringAnimation} exiting={exitingAnimation}>
             <HStack justifyContent="space-between" alignItems="center">
                 <HStack alignItems="center" spacing="sp4">
                     <Text variant="label" color="textSubdued">
diff --git a/suite-native/module-trading/src/hooks/useSectionList.tsx b/suite-native/module-trading/src/hooks/useSectionList.tsx
index 2799dc64c180..7122cd47b8b5 100644
--- a/suite-native/module-trading/src/hooks/useSectionList.tsx
+++ b/suite-native/module-trading/src/hooks/useSectionList.tsx
@@ -1,5 +1,5 @@
 import { ReactElement, ReactNode, useMemo } from 'react';
-import { FadeIn, FadeInUp, FadeOut, FadeOutUp } from 'react-native-reanimated';
+import { FadeIn, FadeOut } from 'react-native-reanimated';
 
 import { UnreachableCaseError } from '@suite-common/suite-utils';
 import { AnimatedBox, Text } from '@suite-native/atoms';
@@ -109,7 +109,7 @@ const renderInternalItem = <T, U>(
     switch (item[0]) {
         case 'sectionHeader':
             return (
-                <AnimatedBox paddingVertical="sp12" entering={FadeInUp} exiting={FadeOutUp}>
+                <AnimatedBox paddingVertical="sp12" entering={FadeIn} exiting={FadeOut}>
                     <Text variant="hint" color="textSubdued">
                         {item[1]}
                     </Text>
diff --git a/suite-native/module-trading/src/screens/TradingScreen.tsx b/suite-native/module-trading/src/screens/TradingScreen.tsx
index f0f59e1b2bd7..e2fa06c0f91f 100644
--- a/suite-native/module-trading/src/screens/TradingScreen.tsx
+++ b/suite-native/module-trading/src/screens/TradingScreen.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import { useSelector } from 'react-redux';
 
 import { useNetInfo } from '@react-native-community/netinfo';
@@ -38,11 +38,14 @@ const TradingScreenContent = () => {
         }
     }, [tradeToBeOpened, navigation]);
 
+    const isLoadingFinished = !isLoading && lastLoadedTimestamp > 0;
+
+    const wasSkeletonDisplayed = useRef(!isLoadingFinished);
+
     if (isInternetReachable === false) {
         return <DeviceOffline />;
     }
 
-    const isLoadingFinished = !isLoading && lastLoadedTimestamp > 0;
     if (isLoadingFinished && !isFullyLoaded) {
         return <ServerOffline onRetryPress={() => setReloadOrdinal(reloadOrdinal + 1)} />;
     }
@@ -52,7 +55,7 @@ const TradingScreenContent = () => {
             <ContextMessage context={Context.tradingBuy} />
             {isFullyLoaded ? (
                 <BuyFormContextProvider>
-                    <BuyForm />
+                    <BuyForm shouldAnimateEntering={wasSkeletonDisplayed.current} />
                 </BuyFormContextProvider>
             ) : (
                 <BuyFormSkeleton />


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx (1)</summary><blockquote>

`26-27`: **Animation props should be optional**

Unlike TradingFooter where animation props are optional, here they are required. Consider making these props optional for consistency with other components in the module, especially if there are cases where animations aren't needed.

```diff
-    enteringAnimation: AnimatedProps<Record<never, unknown>>['entering'];
-    exitingAnimation: AnimatedProps<Record<never, unknown>>['exiting'];
+    enteringAnimation?: AnimatedProps<Record<never, unknown>>['entering'];
+    exitingAnimation?: AnimatedProps<Record<never, unknown>>['exiting'];
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between bff6da97ec75519d3fbd8fccb24b07cf37fff90b and 5012504a84817ca6287855008ffd7e9c83b77059.

</details>

<details>
<summary>📒 Files selected for processing (13)</summary>

* `suite-native/atoms/src/Card/Card.tsx` (5 hunks)
* `suite-native/module-trading/src/components/buy/BuyCard.tsx` (3 hunks)
* `suite-native/module-trading/src/components/buy/BuyForm.tsx` (2 hunks)
* `suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx` (1 hunks)
* `suite-native/module-trading/src/components/buy/BuyHeader.tsx` (2 hunks)
* `suite-native/module-trading/src/components/buy/Confirmation.tsx` (3 hunks)
* `suite-native/module-trading/src/components/buy/PaymentCard.tsx` (2 hunks)
* `suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx` (2 hunks)
* `suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx` (1 hunks)
* `suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx` (4 hunks)
* `suite-native/module-trading/src/components/general/TradingFooter.tsx` (1 hunks)
* `suite-native/module-trading/src/hooks/useSectionList.tsx` (2 hunks)
* `suite-native/module-trading/src/screens/TradingScreen.tsx` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (4)</summary>

<details>
<summary>suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx (1)</summary><blockquote>

<details>
<summary>suite-native/atoms/src/Card/Card.tsx (1)</summary>

* `Card` (94-140)

</details>

</blockquote></details>
<details>
<summary>suite-native/module-trading/src/screens/TradingScreen.tsx (1)</summary><blockquote>

<details>
<summary>suite-native/module-trading/src/components/buy/BuyForm.tsx (1)</summary>

* `BuyForm` (84-97)

</details>

</blockquote></details>
<details>
<summary>suite-native/module-trading/src/components/buy/PaymentCard.tsx (4)</summary><blockquote>

<details>
<summary>suite-native/atoms/src/Card/Card.tsx (1)</summary>

* `Card` (94-140)

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/PaymentMethodPicker.tsx (1)</summary>

* `PaymentMethodPicker` (16-78)

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/CountryOfResidencePicker.tsx (1)</summary>

* `CountryOfResidencePicker` (11-58)

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/TradingProviderPicker.tsx (1)</summary>

* `TradingProviderPicker` (23-98)

</details>

</blockquote></details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx (4)</summary><blockquote>

<details>
<summary>suite-native/atoms/src/Skeleton/BoxSkeleton.tsx (1)</summary>

* `BoxSkeleton` (50-102)

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyHeader.tsx (1)</summary>

* `BuyHeader` (10-20)

</details>
<details>
<summary>suite-native/atoms/src/Card/Card.tsx (1)</summary>

* `Card` (94-140)

</details>
<details>
<summary>suite-native/module-trading/src/components/general/TradingFooter.tsx (1)</summary>

* `TradingFooter` (14-42)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: Unit Tests
* GitHub Check: Type Checking
* GitHub Check: Linting and formatting
* GitHub Check: prepare_android_test_app
* GitHub Check: Analyze with CodeQL (javascript)

</details>

<details>
<summary>🔇 Additional comments (38)</summary><blockquote>

<details>
<summary>suite-native/module-trading/src/hooks/useSectionList.tsx (1)</summary>

`112-117`: **Apply FadeIn/FadeOut to section headers for consistent animations**  
Switching the section header’s `entering`/`exiting` props to `FadeIn`/`FadeOut` simplifies the behavior and matches the refactored animation approach in other components. This change looks correct and maintains a cohesive visual experience.

</details>
<details>
<summary>suite-native/atoms/src/Card/Card.tsx (5)</summary>

`18-18`: **Good addition of the `noShadow` option to CardProps**

This additional prop provides flexibility to consumers who need to conditionally disable the shadow effect.

---

`34-35`: **LGTM - Clean implementation of the noShadow parameter**

The type definition is properly updated to include the new parameter.

---

`62-65`: **Well implemented shadow conditional logic**

The shadow is now only applied when `noShadow` is false, which matches the expected behavior based on the prop name. Good use of the `extend` pattern with conditions.

---

`96-104`: **LGTM - Good default value for backward compatibility**

Setting `noShadow = false` as default ensures that existing Card usages won't be affected by this change.

---

`123-123`: **Properly passing the noShadow prop to style application**

This completes the implementation by ensuring the prop value is used when applying styles.

</details>
<details>
<summary>suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx (3)</summary>

`2-2`: **LGTM - Added Platform import for OS detection**

Correctly imported the Platform API to implement OS-specific behavior.

---

`23-23`: **Good platform-specific shadow handling**

Setting `noShadow` to true for Android aligns with the PR objective to remove shadows for the country list on Android to enable better FadeIn animations.

---

`28-28`: **LGTM - Correctly implementing platform-specific styling**

The Card component now correctly receives the platform-specific noShadow prop, improving animation appearance on Android as intended.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyHeader.tsx (2)</summary>

`1-1`: **Simplified animation imports**

Removed the unused FadeIn import, keeping only the animations actually used by the component.

---

`11-15`: **Updated animation behavior and added horizontal padding**

The changes properly implement the intended animation behavior:
1. Removed FadeIn animation in favor of either no animation (when form recently mounted) or FadeInUp
2. Added horizontal padding to align with other headers in the app

This matches the PR objectives to change animation behavior and improve header positioning.

</details>
<details>
<summary>suite-native/module-trading/src/screens/TradingScreen.tsx (3)</summary>

`1-1`: **Appropriate import addition**

Adding `useRef` to the imports is necessary for tracking the skeleton display state.

---

`41-44`: **Good use of refs to track initial skeleton state**

Using a ref to track whether the skeleton was displayed is an effective approach that won't trigger unnecessary re-renders. This is the correct pattern for remembering a value from the initial render.

---

`58-58`: **Effective animation control implementation**

Passing the skeleton display state to the `BuyForm` component allows for conditional animation control, improving the user experience by preventing unnecessary animations when the form is shown immediately.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyCard.tsx (4)</summary>

`1-1`: **Appropriate Platform import addition**

Adding the Platform import is necessary for the platform-specific animation logic.

---

`27-27`: **Good prop addition for animation control**

Adding the optional `shouldAnimateEntering` prop provides the necessary control over animations from parent components.

---

`64-65`: **Platform-specific animation handling**

Conditionally applying the FadeIn animation only on iOS demonstrates awareness of platform-specific animation behaviors, which aligns with the PR objective to fix visual issues on different platforms.

---

`67-67`: **Effective conditional animation application**

Using the dynamically determined animation value provides the flexibility needed to control the component's entrance behavior based on both platform and parent component state.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/Confirmation.tsx (4)</summary>

`1-1`: **Good animation imports update**

Replacing the FadeInDown import with AnimatedProps provides better type safety for the animation props.

---

`11-11`: **Improved prop type for animations**

Changing from a boolean flag to a strongly-typed animation prop provides better control and makes the animation behavior more explicit and type-safe.

---

`16-16`: **Updated function signature for animation props**

The updated parameter destructuring correctly matches the new prop type, maintaining consistency.

---

`25-25`: **Simplified animation application**

Directly applying the passed animation prop instead of conditional logic simplifies the component and delegates animation decisions to the parent component, improving separation of concerns.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/PaymentCard.tsx (5)</summary>

`1-2`: **Appropriate imports for platform-specific animations**

Adding Platform and the additional animation types is necessary for the enhanced animation logic.

---

`12-12`: **Good prop addition for animation control**

Adding the optional `shouldAnimateEntering` prop aligns with the pattern used in other components and provides consistent animation control.

---

`15-25`: **Well-structured platform-specific animation logic**

The helper function cleanly encapsulates the complex platform-specific animation logic, making it easy to understand and maintain. The different animation behavior for Android addresses the visual issues mentioned in the PR objectives.

---

`27-27`: **Appropriate exit animation handling**

The platform-specific exit animation logic ensures consistent visual behavior when elements are removed.

---

`29-42`: **Clean component implementation with animation helpers**

The component implementation is clean and straightforward, using the helper functions to determine animations while maintaining a simple render function.

</details>
<details>
<summary>suite-native/module-trading/src/components/general/TradingFooter.tsx (2)</summary>

`3-11`: **Improved animation control with explicit animation props**

The component now accepts explicit animation props (`enteringAnimation` and `exitingAnimation`) instead of using a boolean flag to determine animations internally. This is a good practice as it provides more flexibility and control to the parent component.

---

`14-21`: **Clean implementation of animation props**

The refactored implementation correctly applies the passed animation props directly to the AnimatedBox, allowing the parent component to have fine-grained control over animations.

</details>
<details>
<summary>suite-native/module-trading/src/components/general/TradeHistory/TradeHistoryButton.tsx (2)</summary>

`2-2`: **Appropriate imports for animation types**

The component now imports AnimatedProps from react-native-reanimated, which is used to properly type the animation props.

---

`50-68`: **Consistent animation implementation**

The component now properly implements the explicit animation control pattern used throughout the trading module.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyFormSkeleton.tsx (2)</summary>

`11-40`: **Well-structured skeleton components with flexible sizing**

The refactoring introduces reusable, type-safe skeleton components that calculate their width based on screen percentage rather than using hardcoded values. This improves adaptability across different screen sizes and provides a more consistent visual experience.

---

`42-57`: **Improved skeleton layout with modular components**

The BuyFormSkeleton implementation is now more maintainable and consistent:
1. Uses the new modular skeleton components with specific width percentages
2. Properly integrates the BuyHeader component
3. Applies Card components consistently for the layout
4. Aligns with the updated TradingFooter props pattern

This creates a more polished loading state that matches the actual form layout better.

</details>
<details>
<summary>suite-native/module-trading/src/components/buy/BuyForm.tsx (5)</summary>

`30-39`: **Clean helper function for animation logic**

The new `getEnteringAnimationForItemsUnderBuyCard` function appropriately centralizes the animation logic, making it easier to understand and maintain. The function correctly handles both the initial loading state and the state after user interaction.

---

`47-51`: **Proper implementation of conditional animation logic**

The component correctly uses the helper function to determine the appropriate animation based on both the form's mounted state and the shouldAnimateEntering prop.

---

`58-61`: **Consistent animation control propagation**

The BuyCard component now receives the shouldAnimateEntering prop, ensuring the animation behavior is consistent with the parent component's expectations.

---

`65-77`: **Coordinated animation control across child components**

The implementation correctly passes:
1. The appropriate enteringAnimation to Confirmation and TradingFooter
2. Both entering and exiting animations to TradeHistoryButton
3. The shouldAnimateEntering prop to PaymentCard

This ensures a coordinated animation experience throughout the form.

---

`84-97`: **Clean prop forwarding in BuyForm**

The BuyForm component now properly accepts the shouldAnimateEntering prop and forwards it to the memoized implementation. This separation of concerns maintains good component architecture while supporting the new animation control pattern.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T16:05:22Z
user: login: vytick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:29:44Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T19:30:12Z
user: login: vytick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:33:04Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T19:33:25Z
user: login: vytick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:46:13Z
user: login: vytick
body: Much nicer. Especially on Android. Great job 👌 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:20:41Z

Review comments:
diff_hunk: @@ -1,5 +1,5 @@
 import { ReactElement, ReactNode, useMemo } from 'react';
-import { FadeIn, FadeInUp, FadeOut, FadeOutUp } from 'react-native-reanimated';
+import { FadeIn, FadeOut } from 'react-native-reanimated';
path: suite-native/module-trading/src/hooks/useSectionList.tsx
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Standardize animations by importing only FadeIn and FadeOut**  
Good cleanup removing the now-unused `FadeInUp` and `FadeOutUp` imports. This aligns the hook with the new animation patterns used across the trading module.  
   
Please run the following to ensure no stale references remain elsewhere in the codebase:

---

🏁 Script executed:

```shell
#!/bin/bash
# Verify no lingering imports or references to the old animation variants
rg -g "*.tsx" "FadeInUp"
rg -g "*.tsx" "FadeOutUp"
```

Length of output: 2289

---



</details>

**Detected residual FadeInUp/FadeOutUp references**  
While `useSectionList.tsx` has been cleaned up, the following files still import or reference `FadeInUp` / `FadeOutUp`. To fully standardize on `FadeIn` / `FadeOut` (or other approved patterns), please replace these variants in:

- suite-native/module-device-onboarding/src/components/SecurityCheckStepCard.tsx  
- suite-native/react-native-graph/src/AnimatedLineGraph.tsx  
- suite-native/module-trading/src/components/buy/BuyForm.tsx  
- suite-native/module-trading/src/components/buy/BuyHeader.tsx  
- suite-native/module-trading/src/components/buy/PaymentCard.tsx  
- suite-native/graph/src/components/GraphError.tsx  
- suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx  

Specifically, update both the imports and any `entering`/`exiting` props to use the new animation variants.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In suite-native/module-trading/src/hooks/useSectionList.tsx at line 2, you have
already removed the unused FadeInUp and FadeOutUp imports. However, other files
still import or reference these old animation variants. To fully standardize,
locate the imports and usage of FadeInUp and FadeOutUp in the listed files and
replace them with FadeIn and FadeOut or other approved animation patterns.
Update both the import statements and any entering/exiting animation props
accordingly to remove all residual references.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-15T16:05:21Z
author_association: NONE
diff_hunk: @@ -62,7 +62,7 @@ export const ProvidersSheet = ({
                         companyName={companyName ?? ''}
                         logo={logo ?? ''}
                         onPress={() => onQuoteSelectCallback(item)}
-                        isSelected={item.orderId === selectedQuote?.orderId}
+                        isSelected={item.exchange === selectedQuote?.exchange}
path: suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fixed provider selection visual bug**

Changed the selection logic to use `exchange` property instead of `orderId`. This fixes the issue where providers were sometimes not visually indicated as selected in the provider picker.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
at line 65, update the selection logic to compare the `exchange` property of the
item with the `exchange` property of the selectedQuote instead of using
`orderId`. This change ensures the correct provider is visually indicated as
selected in the provider picker.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-15T16:05:22Z
author_association: NONE
diff_hunk: @@ -1,5 +1,5 @@
 import { ReactElement, ReactNode, useMemo } from 'react';
-import { FadeIn, FadeInUp, FadeOut, FadeOutUp } from 'react-native-reanimated';
+import { FadeIn, FadeOut } from 'react-native-reanimated';
path: suite-native/module-trading/src/hooks/useSectionList.tsx
user: login: vytick
body: there are different animations for different usecases. close this
created_at: 2025-05-15T19:29:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,5 @@
 import { ReactElement, ReactNode, useMemo } from 'react';
-import { FadeIn, FadeInUp, FadeOut, FadeOutUp } from 'react-native-reanimated';
+import { FadeIn, FadeOut } from 'react-native-reanimated';
path: suite-native/module-trading/src/hooks/useSectionList.tsx
user: login: coderabbitai[bot]
body: @vytick I understand - you're right that different UI elements need different animation styles depending on their context and purpose. I'll close this review comment as requested.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vytick
PR: trezor/trezor-suite#18977
File: suite-native/module-trading/src/hooks/useSectionList.tsx:2-2
Timestamp: 2025-05-15T19:30:11.194Z
Learning: In the Trezor Suite mobile app, different UI elements intentionally use different animation variants (FadeIn, FadeOut, FadeInUp, FadeOutUp, etc.) based on their specific use cases and visual requirements. There's no need to standardize all animations to a single type.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vytick I understand - you're right that different UI elements need different animation styles depending on their context and purpose. I'll close this review comment as requested.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vytick
PR: trezor/trezor-suite#18977
File: suite-native/module-trading/src/hooks/useSectionList.tsx:2-2
Timestamp: 2025-05-15T19:30:11.194Z
Learning: In the Trezor Suite mobile app, different UI elements intentionally use different animation variants (FadeIn, FadeOut, FadeInUp, FadeOutUp, etc.) based on their specific use cases and visual requirements. There's no need to standardize all animations to a single type.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-15T19:30:12Z
author_association: NONE
diff_hunk: @@ -62,7 +62,7 @@ export const ProvidersSheet = ({
                         companyName={companyName ?? ''}
                         logo={logo ?? ''}
                         onPress={() => onQuoteSelectCallback(item)}
-                        isSelected={item.orderId === selectedQuote?.orderId}
+                        isSelected={item.exchange === selectedQuote?.exchange}
path: suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
user: login: vytick
body: okay, that is what just happened in this PR, what do you suggest again?
created_at: 2025-05-15T19:33:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -62,7 +62,7 @@ export const ProvidersSheet = ({
                         companyName={companyName ?? ''}
                         logo={logo ?? ''}
                         onPress={() => onQuoteSelectCallback(item)}
-                        isSelected={item.orderId === selectedQuote?.orderId}
+                        isSelected={item.exchange === selectedQuote?.exchange}
path: suite-native/module-trading/src/components/general/ProviderSheet/ProvidersSheet.tsx
user: login: coderabbitai[bot]
body: @vytick I apologize for the confusion! You're absolutely right - the change from `item.orderId === selectedQuote?.orderId` to `item.exchange === selectedQuote?.exchange` is exactly what's already happening in this PR. I mistakenly generated a review comment suggesting a change that you've already implemented. This is the correct approach to fix the provider selection visual bug. Sorry for the redundant suggestion!


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vytick I apologize for the confusion! You're absolutely right - the change from `item.orderId === selectedQuote?.orderId` to `item.exchange === selectedQuote?.exchange` is exactly what's already happening in this PR. I mistakenly generated a review comment suggesting a change that you've already implemented. This is the correct approach to fix the provider selection visual bug. Sorry for the redundant suggestion!</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-15T19:33:25Z
author_association: NONE
diff_hunk: @@ -8,14 +9,34 @@ import { TradingProviderPicker } from './TradingProviderPicker';
 
 export type PaymentCardProps = {
     isFormMountedRecently?: boolean;
+    shouldAnimateEntering?: boolean;
 };
 
-export const PaymentCard = ({ isFormMountedRecently }: PaymentCardProps) => (
-    <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
-        <Card noPadding>
-            <PaymentMethodPicker />
-            <CountryOfResidencePicker />
-            <TradingProviderPicker />
-        </Card>
-    </AnimatedBox>
-);
+const getEnteringAnimation = (isFormMountedRecently?: boolean, shouldAnimateEntering?: boolean) => {
+    if (Platform.OS === 'android') {
path: suite-native/module-trading/src/components/buy/PaymentCard.tsx
user: login: vytick
body: please, add comment why android works different
created_at: 2025-05-15T19:39:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,14 +9,34 @@ import { TradingProviderPicker } from './TradingProviderPicker';
 
 export type PaymentCardProps = {
     isFormMountedRecently?: boolean;
+    shouldAnimateEntering?: boolean;
 };
 
-export const PaymentCard = ({ isFormMountedRecently }: PaymentCardProps) => (
-    <AnimatedBox entering={isFormMountedRecently ? FadeIn : FadeInDown} exiting={FadeOutDown}>
-        <Card noPadding>
-            <PaymentMethodPicker />
-            <CountryOfResidencePicker />
-            <TradingProviderPicker />
-        </Card>
-    </AnimatedBox>
-);
+const getEnteringAnimation = (isFormMountedRecently?: boolean, shouldAnimateEntering?: boolean) => {
+    if (Platform.OS === 'android') {
+        return isFormMountedRecently ? undefined : StretchInY;
+    }
+
+    if (isFormMountedRecently) {
+        return shouldAnimateEntering ? FadeIn : undefined;
+    }
+
+    return FadeInDown;
+};
+
+const getExitingAnimation = () => (Platform.OS === 'android' ? StretchOutY : FadeOutUp);
path: suite-native/module-trading/src/components/buy/PaymentCard.tsx
user: login: vytick
body: also here pls add comment, why this difference
created_at: 2025-05-15T19:40:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,11 +20,12 @@ const wrapperStyle = prepareNativeStyle(({ spacings }) => ({
 
 export const CountryListItem = ({ label, onPress, value, isSelected }: CountryListItemProps) => {
     const { applyStyle } = useNativeStyles();
+    const noShadow = Platform.OS === 'android';
path: suite-native/module-trading/src/components/general/CountrySheet/CountryListItem.tsx
user: login: vytick
body: why - comment
created_at: 2025-05-15T19:40:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,15 +54,17 @@ const useAnimatedBorderStyle = (isAmountInputActive: boolean) => {
     }));
 };
 
-export const BuyCard = ({ isAmountInputActive }: BuyCardProps) => {
+export const BuyCard = ({ isAmountInputActive, shouldAnimateEntering }: BuyCardProps) => {
     const { applyStyle } = useNativeStyles();
     const animatedStyle = useAnimatedBorderStyle(isAmountInputActive);
     const { watch } = useTradingBuyFormContext();
 
     const asset = watch('asset');
 
+    const enteringAnimation = shouldAnimateEntering && Platform.OS === 'ios' ? FadeIn : undefined;
path: suite-native/module-trading/src/components/buy/BuyCard.tsx
user: login: vytick
body: please, comment why the difference
created_at: 2025-05-15T19:45:57Z
author_association: CONTRIBUTOR

PR comments:
user: login: jbazant
created_at: 2025-05-15T16:02:30Z
body: @coderabbitai review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-05-15T16:05:18Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes introduce new props and refactor animation logic across several components in the trading module. The `Card` component now accepts a `noShadow` prop to control shadow rendering, with Android-specific handling in `CountryListItem`. The `BuyCard`, `PaymentCard`, and `BuyForm` components receive new props for conditional animation control, and animation logic is centralized and delegated via explicit animation props rather than internal boolean flags. Skeleton UI in `BuyFormSkeleton` is modularized for flexible layouts. Selection logic in `ProvidersSheet` is updated to use the `exchange` property. The `TradingScreen` tracks whether the skeleton was displayed to determine animation behavior in `BuyForm`. No breaking changes to exported component signatures are introduced except for updated prop types to support the new animation and shadow control patterns.

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=trezor/trezor-suite&utm_content=18977):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: jbazant
created_at: 2025-05-16T06:26:47Z
body: /rebase
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-16T06:27:05Z
body: Start rebasing: https://github.com/trezor/trezor-suite/actions/runs/15062115281
author_association: NONE