Repository: stellar/freighter-mobile
Pull request #97: Fix spacing inconsistencies across different devices
Author: login: Brunonascdev
Labels: name: bug
description: Something isn't working
Created: 2025-04-16T15:17:30Z | Merged: 2025-04-21T14:08:07Z

Fix spacing inconsistencies across different devices

closes issue #93 and #92

## iOS (iPhone 14 Pro)
https://github.com/user-attachments/assets/9eca4f76-e729-4ed0-ad75-36a28535e1f0

## Android (Galaxy S21)
https://github.com/user-attachments/assets/91512bc7-896e-4592-8791-8a8d43c52257

## iOS Small (iPhone SE)
https://github.com/user-attachments/assets/72065ad6-900b-4999-af6a-902e4d6999c2

## Android Small (720x1280)
[smallandroid.webm](https://github.com/user-attachments/assets/705c3f8a-6806-40db-acf4-ce7bc9b92160)




Diff:
diff --git a/ios/freighter-mobile/PrivacyInfo.xcprivacy b/ios/freighter-mobile/PrivacyInfo.xcprivacy
index c6b452ea2..53631f09b 100644
--- a/ios/freighter-mobile/PrivacyInfo.xcprivacy
+++ b/ios/freighter-mobile/PrivacyInfo.xcprivacy
@@ -16,18 +16,18 @@
 		</dict>
 		<dict>
 			<key>NSPrivacyAccessedAPIType</key>
-			<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
+			<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
 			<key>NSPrivacyAccessedAPITypeReasons</key>
 			<array>
-				<string>CA92.1</string>
+				<string>35F9.1</string>
 			</array>
 		</dict>
 		<dict>
 			<key>NSPrivacyAccessedAPIType</key>
-			<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
+			<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
 			<key>NSPrivacyAccessedAPITypeReasons</key>
 			<array>
-				<string>35F9.1</string>
+				<string>CA92.1</string>
 			</array>
 		</dict>
 		<dict>
@@ -35,8 +35,8 @@
 			<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
 			<key>NSPrivacyAccessedAPITypeReasons</key>
 			<array>
-				<string>E174.1</string>
 				<string>85F4.1</string>
+				<string>E174.1</string>
 			</array>
 		</dict>
 	</array>
diff --git a/jest.setup.js b/jest.setup.js
index ae9150588..6b6ff62a4 100644
--- a/jest.setup.js
+++ b/jest.setup.js
@@ -1,6 +1,41 @@
 /* eslint-disable @fnando/consistent-import/consistent-import */
 /* eslint-disable import/extensions */
 import mockClipboard from "@react-native-clipboard/clipboard/jest/clipboard-mock.js";
+import mockRNDeviceInfo from "react-native-device-info/jest/react-native-device-info-mock";
+
+// Create a direct mock for the specific functions from react-native-responsive-screen
+// This ensures these functions are defined before any module imports them
+global.heightPercentageToDP = jest.fn((height) => height);
+global.widthPercentageToDP = jest.fn((width) => width);
+
+// Mock the module itself
+jest.mock("react-native-responsive-screen", () => ({
+  heightPercentageToDP: global.heightPercentageToDP,
+  widthPercentageToDP: global.widthPercentageToDP,
+}));
+
+// Mock dimensions helper explicitly
+jest.mock("helpers/dimensions", () => ({
+  pxValue: (value) => value,
+  px: (value) => `${value}px`,
+  fsValue: (value) => value,
+  fs: (value) => `${value}px`,
+  deviceAspectRatio: 0.5,
+  calculateEdgeSpacing: (baseSpacing, options) => {
+    const { multiplier = 1, toNumber = false } = options || {};
+    const scaledValue = baseSpacing * multiplier;
+    return toNumber ? scaledValue : `${scaledValue}px`;
+  },
+}));
+
+// Mock react-native
+jest.mock("react-native", () => {
+  const RN = jest.requireActual("react-native");
+  RN.Dimensions = {
+    get: jest.fn().mockReturnValue({ width: 400, height: 800 }),
+  };
+  return RN;
+});
 
 // Mock navigation
 jest.mock("@react-navigation/native", () => {
@@ -59,12 +94,6 @@ jest.mock("react-native-safe-area-context", () => {
   };
 });
 
-// Mock react-native-responsive-screen
-jest.mock("react-native-responsive-screen", () => ({
-  widthPercentageToDP: jest.fn((width) => width),
-  heightPercentageToDP: jest.fn((height) => height),
-}));
-
 jest.mock("@react-native-clipboard/clipboard", () => mockClipboard);
 
 jest.mock("@stablelib/base64", () => ({
@@ -135,3 +164,5 @@ jest.mock("@react-navigation/bottom-tabs", () => ({
     Group: jest.fn(),
   }),
 }));
+
+jest.mock("react-native-device-info", () => mockRNDeviceInfo);
diff --git a/src/components/BottomSheet.tsx b/src/components/BottomSheet.tsx
index 62a9d8943..6d18d6497 100644
--- a/src/components/BottomSheet.tsx
+++ b/src/components/BottomSheet.tsx
@@ -8,6 +8,7 @@ import {
 import { BottomSheetViewProps } from "@gorhom/bottom-sheet/lib/typescript/components/bottomSheetView/types";
 import Icon from "components/sds/Icon";
 import { Text } from "components/sds/Typography";
+import { calculateEdgeSpacing } from "helpers/dimensions";
 import useColors from "hooks/useColors";
 import React, { useCallback } from "react";
 import { TouchableOpacity, View } from "react-native";
@@ -105,7 +106,9 @@ const BottomSheet: React.FC<BottomSheetProps> = ({
       <BottomSheetView
         className="flex-1 bg-background-primary pl-6 pr-6 pt-6 gap-6"
         style={{
-          paddingBottom: insets.bottom,
+          paddingBottom: calculateEdgeSpacing(insets.bottom, {
+            toNumber: true,
+          }) as number,
         }}
         {...bottomSheetViewProps}
       >
diff --git a/src/components/CustomNavigationHeader.tsx b/src/components/CustomNavigationHeader.tsx
index fd2cb24c3..10af6d8bd 100644
--- a/src/components/CustomNavigationHeader.tsx
+++ b/src/components/CustomNavigationHeader.tsx
@@ -3,7 +3,8 @@ import { NativeStackHeaderProps } from "@react-navigation/native-stack";
 import Icon from "components/sds/Icon";
 import { Text } from "components/sds/Typography";
 import { THEME } from "config/theme";
-import { px } from "helpers/dimensions";
+import { isAndroid } from "helpers/device";
+import { calculateEdgeSpacing, px } from "helpers/dimensions";
 import React from "react";
 import { TouchableOpacity } from "react-native";
 import { EdgeInsets, useSafeAreaInsets } from "react-native-safe-area-context";
@@ -14,7 +15,8 @@ interface StyledProps {
 }
 
 const StyledContainer = styled.View<StyledProps>`
-  padding-top: ${({ $insets }: StyledProps) => px($insets.top)};
+  padding-top: ${({ $insets }: StyledProps) =>
+    calculateEdgeSpacing($insets.top)};
   padding-left: ${px(24)};
   padding-right: ${px(24)};
   padding-bottom: ${px(16)};
@@ -45,7 +47,10 @@ const CustomNavigationHeader = (
           pressOpacity: 0.5,
         })
       ) : (
-        <TouchableOpacity onPress={() => navigation.goBack()}>
+        <TouchableOpacity
+          onPress={() => navigation.goBack()}
+          className={isAndroid ? "p-2" : undefined}
+        >
           <Icon.ArrowLeft color={THEME.colors.base.secondary} />
         </TouchableOpacity>
       )}
diff --git a/src/components/layout/BaseLayout.tsx b/src/components/layout/BaseLayout.tsx
index 2e7eff828..ffd4a9890 100644
--- a/src/components/layout/BaseLayout.tsx
+++ b/src/components/layout/BaseLayout.tsx
@@ -2,6 +2,7 @@
 import { ScrollableKeyboardView } from "components/ScrollableKeyboardView";
 import { DEFAULT_PADDING } from "config/constants";
 import { THEME } from "config/theme";
+import { calculateEdgeSpacing } from "helpers/dimensions";
 import React from "react";
 import { EdgeInsets, useSafeAreaInsets } from "react-native-safe-area-context";
 import styled from "styled-components/native";
@@ -34,12 +35,16 @@ const StyledSafeAreaView = styled.View<StyledViewProps>`
   flex: 1;
   background-color: ${({ $backgroundColor }: StyledViewProps) =>
     $backgroundColor};
-  padding-top: ${({ $insets, $insetsConfig }: StyledViewProps) =>
-    $insetsConfig?.top ? $insets.top : 0}px;
+  padding-top: ${({ $insets, $insetsConfig }: StyledViewProps) => {
+    if (!$insetsConfig?.top) return 0;
+    return calculateEdgeSpacing($insets.top, { toNumber: true });
+  }}px;
   padding-right: ${({ $insets, $insetsConfig }: StyledViewProps) =>
     ($insetsConfig?.right ? $insets.right : 0) + DEFAULT_PADDING}px;
-  padding-bottom: ${({ $insets, $insetsConfig }: StyledViewProps) =>
-    $insetsConfig?.bottom ? $insets.bottom : 0}px;
+  padding-bottom: ${({ $insets, $insetsConfig }: StyledViewProps) => {
+    if (!$insetsConfig?.bottom) return 0;
+    return calculateEdgeSpacing($insets.bottom, { toNumber: true });
+  }}px;
   padding-left: ${({ $insets, $insetsConfig }: StyledViewProps) =>
     ($insetsConfig?.left ? $insets.left : 0) + DEFAULT_PADDING}px;
 `;
diff --git a/src/components/layout/OnboardLayout.tsx b/src/components/layout/OnboardLayout.tsx
index 7a4822636..a0ea33d44 100644
--- a/src/components/layout/OnboardLayout.tsx
+++ b/src/components/layout/OnboardLayout.tsx
@@ -4,10 +4,9 @@ import { Button } from "components/sds/Button";
 import Icon from "components/sds/Icon";
 import { Display, Text } from "components/sds/Typography";
 import { PALETTE, THEME } from "config/theme";
-import { px } from "helpers/dimensions";
+import { calculateEdgeSpacing, px } from "helpers/dimensions";
 import { t } from "i18next";
 import React from "react";
-import { Platform } from "react-native";
 import { EdgeInsets, useSafeAreaInsets } from "react-native-safe-area-context";
 import styled from "styled-components/native";
 
@@ -32,7 +31,8 @@ interface StyledProps {
 const StyledContainer = styled.View<StyledProps>`
   padding-left: ${px(24)};
   padding-right: ${px(24)};
-  padding-bottom: ${({ $insets }: StyledProps) => px($insets.bottom)};
+  padding-bottom: ${({ $insets }: StyledProps) =>
+    calculateEdgeSpacing($insets.bottom)};
   flex: 1;
   justify-content: space-between;
   background-color: ${THEME.colors.background.default};
@@ -47,7 +47,6 @@ const StyledContentContainer = styled.View`
 const FooterContainer = styled.View`
   gap: ${px(24)};
   background-color: ${THEME.colors.background.default};
-  padding-bottom: ${Platform.OS === "ios" ? 0 : px(24)};
 `;
 
 const FooterNoteText = styled(Text)`
diff --git a/src/components/primitives/Menu.tsx b/src/components/primitives/Menu.tsx
index 35faaee31..01151ddac 100644
--- a/src/components/primitives/Menu.tsx
+++ b/src/components/primitives/Menu.tsx
@@ -5,7 +5,7 @@ import * as DropdownMenu from "zeego/dropdown-menu";
 export const MenuRoot = DropdownMenu.Root;
 export const MenuTrigger = DropdownMenu.create(
   (props: React.ComponentProps<typeof DropdownMenu.Trigger>) => (
-    <DropdownMenu.Trigger {...props} asChild>
+    <DropdownMenu.Trigger className="p-2" {...props} asChild>
       <View>{props.children}</View>
     </DropdownMenu.Trigger>
   ),
diff --git a/src/components/screens/AddAssetScreen/AddAssetBottomSheetContent.tsx b/src/components/screens/AddAssetScreen/AddAssetBottomSheetContent.tsx
index f6f8e8d21..ef716d7e3 100644
--- a/src/components/screens/AddAssetScreen/AddAssetBottomSheetContent.tsx
+++ b/src/components/screens/AddAssetScreen/AddAssetBottomSheetContent.tsx
@@ -126,7 +126,6 @@ const AddAssetBottomSheetContent: React.FC<AddAssetBottomSheetContentProps> = ({
           </Button>
         </View>
       </View>
-      <View className="mt-6" />
     </View>
   );
 };
diff --git a/src/components/screens/AddAssetScreen/AddAssetScreen.tsx b/src/components/screens/AddAssetScreen/AddAssetScreen.tsx
index 2e0769f5b..aa178c74d 100644
--- a/src/components/screens/AddAssetScreen/AddAssetScreen.tsx
+++ b/src/components/screens/AddAssetScreen/AddAssetScreen.tsx
@@ -359,6 +359,7 @@ const AddAssetScreen: React.FC<AddAssetScreenProps> = ({ navigation }) => {
           onChangeText={handleSearch}
           fieldSize="lg"
           autoCapitalize="none"
+          autoCorrect={false}
           leftElement={
             <Icon.SearchMd size={16} color={themeColors.foreground.primary} />
           }
diff --git a/src/components/screens/LockScreen.tsx b/src/components/screens/LockScreen.tsx
index a0ae8c9fd..08fb9e4a2 100644
--- a/src/components/screens/LockScreen.tsx
+++ b/src/components/screens/LockScreen.tsx
@@ -13,6 +13,7 @@ import { getActiveAccountPublicKey, useAuthenticationStore } from "ducks/auth";
 import { px } from "helpers/dimensions";
 import useAppTranslation from "hooks/useAppTranslation";
 import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { View } from "react-native";
 import styled from "styled-components/native";
 
 type LockScreenProps = NativeStackScreenProps<
@@ -38,10 +39,6 @@ const StyledFormContainer = styled.View`
   gap: ${px(8)};
 `;
 
-const ForgotPasswordContainer = styled.View`
-  margin-bottom: ${px(32)};
-`;
-
 const StyledInputContainer = styled.View`
   width: 100%;
   gap: ${px(12)};
@@ -107,13 +104,7 @@ export const LockScreen: React.FC<LockScreenProps> = ({ navigation }) => {
   }, []);
 
   return (
-    <BaseLayout
-      useSafeArea
-      useKeyboardAvoidingView
-      insets={{
-        bottom: false,
-      }}
-    >
+    <BaseLayout useSafeArea useKeyboardAvoidingView>
       <Container>
         <StyledIconContainer>
           <FreighterLogo width={px(48)} height={px(48)} />
@@ -129,6 +120,7 @@ export const LockScreen: React.FC<LockScreenProps> = ({ navigation }) => {
               isPassword
               placeholder={t("lockScreen.passwordInputPlaceholder")}
               fieldSize="lg"
+              autoCapitalize="none"
               value={passwordValue}
               onChangeText={handlePasswordChange}
               error={error}
@@ -144,11 +136,11 @@ export const LockScreen: React.FC<LockScreenProps> = ({ navigation }) => {
             </Button>
           </StyledInputContainer>
         </StyledFormContainer>
-        <ForgotPasswordContainer>
+        <View>
           <Button secondary lg onPress={() => logout(true)}>
             {t("lockScreen.forgotPasswordButtonText")}
           </Button>
-        </ForgotPasswordContainer>
+        </View>
       </Container>
     </BaseLayout>
   );
diff --git a/src/components/screens/WelcomeScreen.tsx b/src/components/screens/WelcomeScreen.tsx
index 23edf705a..8076e8962 100644
--- a/src/components/screens/WelcomeScreen.tsx
+++ b/src/components/screens/WelcomeScreen.tsx
@@ -28,11 +28,6 @@ const StyledDisplay = styled(Display)`
 
 const StyledIconContainer = styled.View`
   align-items: center;
-  margin-top: ${px(32)};
-`;
-
-const StyledTermsTextContainer = styled.View`
-  margin-bottom: ${px(70)};
 `;
 
 const StyledTermsText = styled(Text)`
@@ -74,14 +69,14 @@ export const WelcomeScreen: React.FC<WelcomeScreenProps> = ({ navigation }) => {
             {t("welcomeScreen.iAlreadyHaveWallet")}
           </Button>
         </View>
-        <StyledTermsTextContainer>
+        <View>
           <StyledTermsText md secondary medium>
             {t("welcomeScreen.terms.byProceeding")}
             <Text md medium url="https://stellar.org/terms-of-service">
               {t("welcomeScreen.terms.termsOfService")}
             </Text>
           </StyledTermsText>
-        </StyledTermsTextContainer>
+        </View>
       </Container>
     </BaseLayout>
   );
diff --git a/src/components/sds/Input/index.tsx b/src/components/sds/Input/index.tsx
index d8078390c..5862eea72 100644
--- a/src/components/sds/Input/index.tsx
+++ b/src/components/sds/Input/index.tsx
@@ -217,6 +217,7 @@ interface InputProps {
   secureTextEntry?: boolean;
   editable?: boolean;
   autoCapitalize?: "none" | "sentences" | "words" | "characters";
+  autoCorrect?: boolean;
   keyboardType?:
     | "default"
     | "number-pad"
@@ -244,6 +245,7 @@ export const Input: React.FC<InputProps> = ({
   placeholder,
   editable = true,
   testID,
+  autoCorrect = true,
   ...props
 }) => {
   const handleCopy = () => {
@@ -301,6 +303,8 @@ export const Input: React.FC<InputProps> = ({
           value={value}
           onChangeText={onChangeText}
           placeholder={placeholder}
+          autoCapitalize={isPassword ? "none" : undefined}
+          autoCorrect={autoCorrect}
           placeholderTextColor={THEME.colors.text.secondary}
           secureTextEntry={isPassword}
           editable={editable}
diff --git a/src/helpers/__mocks__/dimensions.ts b/src/helpers/__mocks__/dimensions.ts
new file mode 100644
index 000000000..085356bb5
--- /dev/null
+++ b/src/helpers/__mocks__/dimensions.ts
@@ -0,0 +1,46 @@
+/**
+ * Mock implementation of the dimensions helper for tests
+ */
+
+export const deviceAspectRatio = 0.5; // Mock aspect ratio
+
+/**
+ * Mock implementation of pxValue that returns the input value
+ */
+export function pxValue(designValue: number): number {
+  return designValue;
+}
+
+/**
+ * Mock implementation of px that returns the input value with 'px' suffix
+ */
+export function px(designValue: number): string {
+  return `${designValue}px`;
+}
+
+/**
+ * Mock implementation of fsValue that returns the input value
+ */
+export function fsValue(designFontSize: number): number {
+  return designFontSize;
+}
+
+/**
+ * Mock implementation of fs that returns the input value with 'px' suffix
+ */
+export function fs(designFontSize: number): string {
+  return `${designFontSize}px`;
+}
+
+/**
+ * Mock implementation of calculateEdgeSpacing
+ */
+export const calculateEdgeSpacing = (
+  baseSpacing: number,
+  options?: { multiplier?: number; toNumber?: boolean },
+): string | number => {
+  const { multiplier = 1, toNumber = false } = options || {};
+  const scaledValue = baseSpacing * multiplier;
+
+  return toNumber ? scaledValue : `${scaledValue}px`;
+};
diff --git a/src/helpers/device.ts b/src/helpers/device.ts
index 428191928..4cf04764c 100644
--- a/src/helpers/device.ts
+++ b/src/helpers/device.ts
@@ -1,4 +1,5 @@
 import { Platform } from "react-native";
+import DeviceInfo from "react-native-device-info";
 
 /**
  * Boolean flag indicating whether the current device is running iOS
@@ -13,3 +14,31 @@ import { Platform } from "react-native";
  * };
  */
 export const isIOS = Platform.OS === "ios";
+
+/**
+ * Boolean flag indicating whether the current device is running Android
+ * Useful for platform-specific code and conditionals
+ *
+ * @example
+ * // Conditionally apply styles or behavior based on platform
+ * const styles = {
+ *   container: {
+ *     marginTop: isAndroid ? 20 : 50,
+ *   }
+ * };
+ */
+export const isAndroid = Platform.OS === "android";
+
+/**
+ * Boolean flag indicating whether the current device has a notch
+ * Useful for conditionals and platform-specific code
+ *
+ * @example
+ * // Conditionally apply styles or behavior based on device notch
+ * const styles = {
+ *   container: {
+ *     marginTop: hasNotch ? 50 : 20,
+ *   }
+ * };
+ */
+export const hasNotch = DeviceInfo.hasNotch();
diff --git a/src/helpers/dimensions.ts b/src/helpers/dimensions.ts
index 9b526cb0d..2fb9e9c20 100644
--- a/src/helpers/dimensions.ts
+++ b/src/helpers/dimensions.ts
@@ -1,3 +1,5 @@
+import { DEFAULT_PADDING } from "config/constants";
+import { hasNotch } from "helpers/device";
 import { Dimensions } from "react-native";
 import { heightPercentageToDP } from "react-native-responsive-screen";
 
@@ -101,3 +103,42 @@ export function fsValue(designFontSize: number): number {
 export function fs(designFontSize: number): string {
   return `${fsValue(designFontSize)}px`;
 }
+
+/**
+ * Calculates responsive spacing that adapts to the device's notch status and platform
+ *
+ * This function provides consistent spacing across different device types by:
+ * - Accepting a base spacing value from design specs
+ * - Applying platform-specific adjustments (Android devices with notch get additional padding)
+ * - Allowing optional multiplier to easily create spacing variants
+ * - Converting the result to responsive pixel values with proper units
+ *
+ * @param {number} baseSpacing - The base spacing value from design specifications
+ * @param {Object} options - Optional configuration parameters
+ * @param {number} options.multiplier - Multiplier to scale the spacing (default: 1)
+ * @returns {string} Responsive spacing value as a string with "px" suffix
+ *
+ * @example
+ * // Basic usage for default spacing
+ * const padding = calculateEdgeSpacing(24);
+ *
+ * @example
+ * // Create double spacing with notch adjustment
+ * const largeMargin = calculateEdgeSpacing(24, { multiplier: 2 });
+ *
+ * @example
+ * // Create spacing with no notch adjustment
+ * const fixedSpacing = calculateEdgeSpacing(16, { toNumber: true });
+ */
+export const calculateEdgeSpacing = (
+  baseSpacing: number,
+  options?: { multiplier?: number; toNumber?: boolean },
+): string | number => {
+  const { multiplier = 1, toNumber = false } = options || {};
+  const scaledBaseSpacing = baseSpacing * multiplier;
+  const notchAdjustment = !hasNotch ? DEFAULT_PADDING : 0;
+
+  return toNumber
+    ? pxValue(scaledBaseSpacing + notchAdjustment)
+    : px(scaledBaseSpacing + notchAdjustment);
+};


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR fixes spacing inconsistencies across different devices and makes several updates to UI components and helper functions. Key changes include new spacing calculation logic using calculateSpacing, removal of the legacy AddAssetScreen component, and updates to context menu and bottom sheet components for improved consistency.

### Reviewed Changes

Copilot reviewed 59 out of 61 changed files in this pull request and generated 1 comment.

<details>
<summary>Show a summary per file</summary>

| File                                                                     | Description                                                                         |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| src/components/screens/AddAssetRightContent.tsx                          | Introduces a new button component with updated spacing and icon usage.              |
| src/components/screens/AddAssetBottomSheetContent.tsx                    | Adds new bottom sheet content with updated spacing and layout tweaks.                |
| src/components/screens/AddAssetScreen.tsx                                | Removed legacy AddAssetScreen component as part of the spacing correction effort.    |
| src/components/primitives/Menu.tsx                                       | Adds padding to the menu trigger for improved tap target area.                      |
| src/components/layout/OnboardLayout.tsx, BaseLayout.tsx, CustomNavigationHeader.tsx | Replaces direct insets with calculateSpacing for consistency across devices. |
| src/components/Spinner.tsx, Modal.tsx, ManageAssetRightContent.tsx         | New and updated components that conform to the refresh spacing strategy.             |
| src/components/ContextMenuButton.tsx, BottomSheet.tsx                     | Enhances menu behavior and adds new optional props for the bottom sheet.              |
| Jest setup and tests                                                      | Updates testing utils and mocks to align with the new spacing logic and component props. |
</details>


<details>
<summary>Files not reviewed (2)</summary>

* **ios/freighter-mobile/PrivacyInfo.xcprivacy**: Language not supported
* **package.json**: Language not supported
</details>



state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T15:18:08Z
user: login: CassioMG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:14:30Z
user: login: CassioMG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:22:19Z
user: login: CassioMG
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:25:45Z
user: login: CassioMG
body: Left a few comments
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:27:13Z
user: login: Brunonascdev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T13:54:51Z

Review comments:
diff_hunk: @@ -41,36 +43,46 @@ const ContextMenuButton: React.FC<ContextMenuButtonProps> = ({
     };
   };
 
+  const renderMenuItem = (item: MenuItem) => (
+    <MenuItemComponent
+      key={item.title ?? Math.random().toString()}
path: src/components/ContextMenuButton.tsx
user: login: Copilot
body: Using Math.random() for key generation can lead to unstable component keys and unnecessary re-renders. Consider using a stable identifier from the action data or an array index as a fallback.
created_at: 2025-04-16T15:18:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,3 +103,42 @@ export function fsValue(designFontSize: number): number {
 export function fs(designFontSize: number): string {
   return `${fsValue(designFontSize)}px`;
 }
+
+/**
+ * Calculates responsive spacing that adapts to the device's notch status and platform
+ *
+ * This function provides consistent spacing across different device types by:
+ * - Accepting a base spacing value from design specs
+ * - Applying platform-specific adjustments (Android devices with notch get additional padding)
+ * - Allowing optional multiplier to easily create spacing variants
+ * - Converting the result to responsive pixel values with proper units
+ *
+ * @param {number} baseSpacing - The base spacing value from design specifications
+ * @param {Object} options - Optional configuration parameters
+ * @param {number} options.multiplier - Multiplier to scale the spacing (default: 1)
+ * @returns {string} Responsive spacing value as a string with "px" suffix
+ *
+ * @example
+ * // Basic usage for default spacing
+ * const padding = calculateSpacing(24);
+ *
+ * @example
+ * // Create double spacing with notch adjustment
+ * const largeMargin = calculateSpacing(24, { multiplier: 2 });
+ *
+ * @example
+ * // Create spacing with no notch adjustment
+ * const fixedSpacing = calculateSpacing(16, { ignoreNotch: true });
path: src/helpers/dimensions.ts
user: login: CassioMG
body: it appears this `ignoreNotch` option doesn't exist
created_at: 2025-04-21T13:14:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,3 +103,42 @@ export function fsValue(designFontSize: number): number {
 export function fs(designFontSize: number): string {
   return `${fsValue(designFontSize)}px`;
 }
+
+/**
+ * Calculates responsive spacing that adapts to the device's notch status and platform
+ *
+ * This function provides consistent spacing across different device types by:
+ * - Accepting a base spacing value from design specs
+ * - Applying platform-specific adjustments (Android devices with notch get additional padding)
+ * - Allowing optional multiplier to easily create spacing variants
+ * - Converting the result to responsive pixel values with proper units
+ *
+ * @param {number} baseSpacing - The base spacing value from design specifications
+ * @param {Object} options - Optional configuration parameters
+ * @param {number} options.multiplier - Multiplier to scale the spacing (default: 1)
+ * @returns {string} Responsive spacing value as a string with "px" suffix
+ *
+ * @example
+ * // Basic usage for default spacing
+ * const padding = calculateSpacing(24);
+ *
+ * @example
+ * // Create double spacing with notch adjustment
+ * const largeMargin = calculateSpacing(24, { multiplier: 2 });
+ *
+ * @example
+ * // Create spacing with no notch adjustment
+ * const fixedSpacing = calculateSpacing(16, { ignoreNotch: true });
+ */
+export const calculateSpacing = (
path: src/helpers/dimensions.ts
user: login: CassioMG
body: `calculateSpacing` seems a bit too generic, what do you think about `calculateEdgeSpacing` ? Since it's being used to calculate top and bottom edge spacing
created_at: 2025-04-21T13:22:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,3 +103,42 @@ export function fsValue(designFontSize: number): number {
 export function fs(designFontSize: number): string {
   return `${fsValue(designFontSize)}px`;
 }
+
+/**
+ * Calculates responsive spacing that adapts to the device's notch status and platform
+ *
+ * This function provides consistent spacing across different device types by:
+ * - Accepting a base spacing value from design specs
+ * - Applying platform-specific adjustments (Android devices with notch get additional padding)
+ * - Allowing optional multiplier to easily create spacing variants
+ * - Converting the result to responsive pixel values with proper units
+ *
+ * @param {number} baseSpacing - The base spacing value from design specifications
+ * @param {Object} options - Optional configuration parameters
+ * @param {number} options.multiplier - Multiplier to scale the spacing (default: 1)
+ * @returns {string} Responsive spacing value as a string with "px" suffix
+ *
+ * @example
+ * // Basic usage for default spacing
+ * const padding = calculateSpacing(24);
+ *
+ * @example
+ * // Create double spacing with notch adjustment
+ * const largeMargin = calculateSpacing(24, { multiplier: 2 });
+ *
+ * @example
+ * // Create spacing with no notch adjustment
+ * const fixedSpacing = calculateSpacing(16, { ignoreNotch: true });
+ */
+export const calculateSpacing = (
+  baseSpacing: number,
+  options?: { multiplier?: number; toNumber?: boolean },
+): string | number => {
+  const { multiplier = 1, toNumber = false } = options || {};
+  const scaledBaseSpacing = baseSpacing * multiplier;
+  const notchAdjustment = !hasNotch ? DEFAULT_PADDING : 0;
path: src/helpers/dimensions.ts
user: login: CassioMG
body: so the issue here is that we need to add an extra padding on the screen edges for devices that DON'T have a notch, is my understanding correct?
created_at: 2025-04-21T13:25:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,3 +103,42 @@ export function fsValue(designFontSize: number): number {
 export function fs(designFontSize: number): string {
   return `${fsValue(designFontSize)}px`;
 }
+
+/**
+ * Calculates responsive spacing that adapts to the device's notch status and platform
+ *
+ * This function provides consistent spacing across different device types by:
+ * - Accepting a base spacing value from design specs
+ * - Applying platform-specific adjustments (Android devices with notch get additional padding)
+ * - Allowing optional multiplier to easily create spacing variants
+ * - Converting the result to responsive pixel values with proper units
+ *
+ * @param {number} baseSpacing - The base spacing value from design specifications
+ * @param {Object} options - Optional configuration parameters
+ * @param {number} options.multiplier - Multiplier to scale the spacing (default: 1)
+ * @returns {string} Responsive spacing value as a string with "px" suffix
+ *
+ * @example
+ * // Basic usage for default spacing
+ * const padding = calculateSpacing(24);
+ *
+ * @example
+ * // Create double spacing with notch adjustment
+ * const largeMargin = calculateSpacing(24, { multiplier: 2 });
+ *
+ * @example
+ * // Create spacing with no notch adjustment
+ * const fixedSpacing = calculateSpacing(16, { ignoreNotch: true });
+ */
+export const calculateSpacing = (
+  baseSpacing: number,
+  options?: { multiplier?: number; toNumber?: boolean },
+): string | number => {
+  const { multiplier = 1, toNumber = false } = options || {};
+  const scaledBaseSpacing = baseSpacing * multiplier;
+  const notchAdjustment = !hasNotch ? DEFAULT_PADDING : 0;
path: src/helpers/dimensions.ts
user: login: Brunonascdev
body: yes! devices with a notch has a padding on the insets, so we don't need to reapply it. 
created_at: 2025-04-21T13:54:51Z
author_association: CONTRIBUTOR

PR comments:
user: login: CassioMG
created_at: 2025-04-17T20:03:38Z
body: @Brunonascdev should this PR be pointing to `bn-manage-assets-data-flow` instead of `main` ?
author_association: CONTRIBUTOR
user: login: Brunonascdev
created_at: 2025-04-21T12:16:53Z
body: > @Brunonascdev should this PR be pointing to `bn-manage-assets-data-flow` instead of `main` ?

makes sense. I just changed the base.
author_association: CONTRIBUTOR