diff --git a/suite-native/accounts/package.json b/suite-native/accounts/package.json index ccd8e6508d7..6ee855c44dd 100644 --- a/suite-native/accounts/package.json +++ b/suite-native/accounts/package.json @@ -24,7 +24,6 @@ "@suite-native/alerts": "workspace:*", "@suite-native/atoms": "workspace:*", "@suite-native/config": "workspace:*", - "@suite-native/feature-flags": "workspace:*", "@suite-native/formatters": "workspace:*", "@suite-native/forms": "workspace:*", "@suite-native/icons": "workspace:*", diff --git a/suite-native/accounts/src/components/AccountsList/AccountsList.tsx b/suite-native/accounts/src/components/AccountsList/AccountsList.tsx index 5c4fa121d93..f1d5542643a 100644 --- a/suite-native/accounts/src/components/AccountsList/AccountsList.tsx +++ b/suite-native/accounts/src/components/AccountsList/AccountsList.tsx @@ -54,7 +54,7 @@ export const AccountsList = ({ return ( <> - + {groups.map(([accountTypeHeader, networkAccounts]) => ( {networkAccounts.map(account => ( diff --git a/suite-native/accounts/src/components/AddAccountsButton.tsx b/suite-native/accounts/src/components/AddAccountsButton.tsx index 1a6f069fc97..be1d5791265 100644 --- a/suite-native/accounts/src/components/AddAccountsButton.tsx +++ b/suite-native/accounts/src/components/AddAccountsButton.tsx @@ -16,7 +16,6 @@ import { selectIsDeviceInViewOnlyMode, selectIsPortfolioTrackerDevice, } from '@suite-common/wallet-core'; -import { FeatureFlag, useFeatureFlag } from '@suite-native/feature-flags'; import { useAccountAlerts } from '../hooks/useAccountAlerts'; @@ -29,15 +28,11 @@ export const AddAccountButton = ({ flowType, testID }: AddAccountButtonProps) => const navigation = useNavigation>(); + const hasDeviceDiscovery = useSelector(selectHasDeviceDiscovery); const isSelectedDevicePortfolioTracker = useSelector(selectIsPortfolioTrackerDevice); - const hasDiscovery = useSelector(selectHasDeviceDiscovery); - const isDeviceConnectEnabled = useFeatureFlag(FeatureFlag.IsDeviceConnectEnabled); const { showViewOnlyAddAccountAlert } = useAccountAlerts(); const isDeviceInViewOnlyMode = useSelector(selectIsDeviceInViewOnlyMode); - const shouldShowAddAccountButton = - isSelectedDevicePortfolioTracker || (isDeviceConnectEnabled && !hasDiscovery); - const navigateToImportScreen = () => { navigation.navigate(RootStackRoutes.AccountsImport, { screen: AccountsImportStackRoutes.SelectNetwork, @@ -58,7 +53,7 @@ export const AddAccountButton = ({ flowType, testID }: AddAccountButtonProps) => }); }; - return shouldShowAddAccountButton ? ( + return ( } colorScheme="tertiaryElevation0" size="medium" + isLoading={hasDeviceDiscovery} + isDisabled={hasDeviceDiscovery} testID={testID} /> - ) : null; + ); }; diff --git a/suite-native/accounts/src/components/SearchableAccountsListHeader.tsx b/suite-native/accounts/src/components/SearchableAccountsListHeader.tsx index beca39dce60..e132c480a33 100644 --- a/suite-native/accounts/src/components/SearchableAccountsListHeader.tsx +++ b/suite-native/accounts/src/components/SearchableAccountsListHeader.tsx @@ -24,7 +24,7 @@ const HEADER_ANIMATION_DURATION = 100; const searchFormContainerStyle = prepareNativeStyle(({ spacings }) => ({ height: 48, - marginBottom: spacings.sp16, + marginBottom: spacings.sp8, })); export const SearchableAccountsListHeader = ({ diff --git a/suite-native/accounts/tsconfig.json b/suite-native/accounts/tsconfig.json index 0b5522c53b3..812ee326ea6 100644 --- a/suite-native/accounts/tsconfig.json +++ b/suite-native/accounts/tsconfig.json @@ -29,7 +29,6 @@ { "path": "../alerts" }, { "path": "../atoms" }, { "path": "../config" }, - { "path": "../feature-flags" }, { "path": "../formatters" }, { "path": "../forms" }, { "path": "../icons" }, diff --git a/suite-native/app/src/navigation/RootStackNavigator.tsx b/suite-native/app/src/navigation/RootStackNavigator.tsx index 51f8407c99e..c287667fe40 100644 --- a/suite-native/app/src/navigation/RootStackNavigator.tsx +++ b/suite-native/app/src/navigation/RootStackNavigator.tsx @@ -18,7 +18,7 @@ import { DevUtilsStackNavigator } from '@suite-native/module-dev-utils'; import { TransactionDetailScreen } from '@suite-native/transactions'; import { OnboardingStackNavigator as LegacyOnboardingStackNavigator } from '@suite-native/module-onboarding-legacy'; import { OnboardingStackNavigator } from '@suite-native/module-onboarding'; -import { ReceiveModalScreen } from '@suite-native/receive'; +import { ReceiveStackNavigator, ReceiveModalScreen } from '@suite-native/receive'; import { AuthorizeDeviceStackNavigator } from '@suite-native/module-authorize-device'; import { AddCoinAccountStackNavigator } from '@suite-native/module-add-accounts'; import { DeviceSettingsStackNavigator } from '@suite-native/module-device-settings'; @@ -94,6 +94,7 @@ export const RootStackNavigator = () => { { options={{ animation: 'slide_from_bottom' }} /> + & { iconName: IconName; colorScheme?: ButtonColorScheme; size?: ButtonSize; style?: NativeStyleObject; + isLoading?: boolean; isDisabled?: boolean; }; const sizeDimensions = { @@ -51,6 +53,7 @@ export const IconButton = ({ style, colorScheme = 'primary', size = 'medium', + isLoading = false, isDisabled = false, ...pressableProps }: IconButtonProps) => { @@ -85,7 +88,11 @@ export const IconButton = ({ style, ]} > - + {isLoading ? ( + + ) : ( + + )} ); }; diff --git a/suite-native/intl/src/en.ts b/suite-native/intl/src/en.ts index 4f65360309e..ce9dc08fc83 100644 --- a/suite-native/intl/src/en.ts +++ b/suite-native/intl/src/en.ts @@ -508,6 +508,7 @@ export const en = { }, }, moduleReceive: { + receiveTitle: 'Receive', screenTitle: '{coinSymbol} Receive address', accountNotFound: 'Account {accountKey} not found.', deviceCancelError: 'Address confirmation canceled.', diff --git a/suite-native/module-add-accounts/src/hooks/useAddCoinAccount.ts b/suite-native/module-add-accounts/src/hooks/useAddCoinAccount.ts index fab3aef550b..b0dedc33a2d 100644 --- a/suite-native/module-add-accounts/src/hooks/useAddCoinAccount.ts +++ b/suite-native/module-add-accounts/src/hooks/useAddCoinAccount.ts @@ -155,6 +155,14 @@ export const useAddCoinAccount = () => { closeActionType: 'close', }); break; + case 'receive': + navigation.replace(RootStackRoutes.ReceiveModal, { + networkSymbol: symbol, + accountType, + accountIndex, + closeActionType: 'back', + }); + break; } }; diff --git a/suite-native/module-add-accounts/src/screens/AddCoinAccountScreen.tsx b/suite-native/module-add-accounts/src/screens/AddCoinAccountScreen.tsx index 2e727632b75..82c567a5e8e 100644 --- a/suite-native/module-add-accounts/src/screens/AddCoinAccountScreen.tsx +++ b/suite-native/module-add-accounts/src/screens/AddCoinAccountScreen.tsx @@ -52,6 +52,7 @@ export const AddCoinAccountScreen = ({ screenHeader={ } > diff --git a/suite-native/module-home/src/screens/HomeScreen/components/PortfolioContent.tsx b/suite-native/module-home/src/screens/HomeScreen/components/PortfolioContent.tsx index d58ccc4041a..f107f19f1c3 100644 --- a/suite-native/module-home/src/screens/HomeScreen/components/PortfolioContent.tsx +++ b/suite-native/module-home/src/screens/HomeScreen/components/PortfolioContent.tsx @@ -7,6 +7,7 @@ import { selectHasDeviceDiscovery, selectIsDeviceAuthorized } from '@suite-commo import { Button, VStack } from '@suite-native/atoms'; import { Assets } from '@suite-native/assets'; import { + ReceiveStackRoutes, RootStackParamList, RootStackRoutes, StackNavigationProps, @@ -23,7 +24,9 @@ export const PortfolioContent = forwardRef((_props, ref) => { const showReceiveButton = isDeviceAuthorized && !hasDiscovery; const handleReceive = () => { - navigation.navigate(RootStackRoutes.ReceiveModal, { closeActionType: 'back' }); + navigation.navigate(RootStackRoutes.ReceiveStack, { + screen: ReceiveStackRoutes.ReceiveAccounts, + }); }; return ( diff --git a/suite-native/navigation/src/navigators.ts b/suite-native/navigation/src/navigators.ts index e0aa9c21021..f034864722b 100644 --- a/suite-native/navigation/src/navigators.ts +++ b/suite-native/navigation/src/navigators.ts @@ -132,7 +132,7 @@ export type AccountsImportStackParamList = { }; }; -export type AddCoinFlowType = 'home' | 'accounts'; +export type AddCoinFlowType = 'home' | 'receive' | 'accounts'; export type AddCoinAccountStackParamList = { [AddCoinAccountStackRoutes.AddCoinAccount]: { @@ -212,6 +212,7 @@ export type RootStackParamList = { [RootStackRoutes.StakingDetail]: { accountKey: AccountKey }; [RootStackRoutes.DeviceSettingsStack]: NavigatorScreenParams; [RootStackRoutes.AddCoinAccountStack]: NavigatorScreenParams; + [RootStackRoutes.ReceiveStack]: NavigatorScreenParams; [RootStackRoutes.SendStack]: NavigatorScreenParams; [RootStackRoutes.CoinEnablingInit]: undefined; [RootStackRoutes.ConnectPopup]: { diff --git a/suite-native/navigation/src/routes.ts b/suite-native/navigation/src/routes.ts index d5804f77ac9..1688932e2e9 100644 --- a/suite-native/navigation/src/routes.ts +++ b/suite-native/navigation/src/routes.ts @@ -10,6 +10,7 @@ export enum RootStackRoutes { AccountSettings = 'AccountSettings', TransactionDetail = 'TransactionDetail', ReceiveModal = 'ReceiveModal', + ReceiveStack = 'ReceiveStack', SendStack = 'SendStack', DeviceSettingsStack = 'DeviceSettingsStack', AddCoinAccountStack = 'AddCoinAccountStack', diff --git a/suite-native/receive/package.json b/suite-native/receive/package.json index 097991cc1a9..2997858c062 100644 --- a/suite-native/receive/package.json +++ b/suite-native/receive/package.json @@ -12,6 +12,7 @@ "dependencies": { "@mobily/ts-belt": "^3.13.1", "@react-navigation/native": "6.1.18", + "@react-navigation/native-stack": "6.11.0", "@reduxjs/toolkit": "1.9.5", "@shopify/react-native-skia": "^1.5.10", "@suite-common/wallet-config": "workspace:*", diff --git a/suite-native/receive/src/components/ReceiveScreenHeader.tsx b/suite-native/receive/src/components/ReceiveScreenHeader.tsx new file mode 100644 index 00000000000..6ab3343d3fe --- /dev/null +++ b/suite-native/receive/src/components/ReceiveScreenHeader.tsx @@ -0,0 +1,81 @@ +import { useEffect } from 'react'; +import { useSelector } from 'react-redux'; + +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; + +import { AccountKey, TokenAddress } from '@suite-common/wallet-types'; +import { + AccountsRootState, + selectAccountLabel, + selectAccountNetworkSymbol, +} from '@suite-common/wallet-core'; +import { HStack, Text } from '@suite-native/atoms'; +import { CryptoIcon } from '@suite-native/icons'; +import { Translation } from '@suite-native/intl'; +import { + GoBackIcon, + RootStackParamList, + RootStackRoutes, + ScreenSubHeader, +} from '@suite-native/navigation'; +import { selectAccountTokenSymbol, TokensRootState } from '@suite-native/tokens'; +import TrezorConnect from '@trezor/connect'; + +type ReceiveScreenHeaderProps = { + accountKey?: AccountKey; + tokenContract?: TokenAddress; +}; + +export const ReceiveScreenHeader = ({ accountKey, tokenContract }: ReceiveScreenHeaderProps) => { + const { + params: { closeActionType }, + } = useRoute>(); + const navigation = useNavigation(); + const accountLabel = useSelector((state: AccountsRootState) => + selectAccountLabel(state, accountKey), + ); + const symbol = useSelector((state: AccountsRootState) => + selectAccountNetworkSymbol(state, accountKey), + ); + const tokenSymbol = useSelector((state: TokensRootState) => + selectAccountTokenSymbol(state, accountKey, tokenContract), + ); + + useEffect(() => { + const unsubscribe = navigation.addListener('beforeRemove', () => { + // When leaving the screen, cancel the request for address on trezor device + TrezorConnect.cancel(); + }); + + return unsubscribe; + }, [navigation]); + + return ( + + + {symbol ? ( + + ) : ( + + )} + + + {symbol && } + {accountLabel && ( + + {accountLabel} + {tokenSymbol && ` - ${tokenSymbol}`} + + )} + + + } + leftIcon={} + /> + ); +}; diff --git a/suite-native/receive/src/index.ts b/suite-native/receive/src/index.ts index c9cac85f1eb..e7f54a878fb 100644 --- a/suite-native/receive/src/index.ts +++ b/suite-native/receive/src/index.ts @@ -1,2 +1,3 @@ -export * from './components/ReceiveAccount'; +export * from './navigation/ReceiveStackNavigator'; +export * from './screens/ReceiveAccountScreen'; export * from './screens/ReceiveModalScreen'; diff --git a/suite-native/receive/src/navigation/ReceiveStackNavigator.tsx b/suite-native/receive/src/navigation/ReceiveStackNavigator.tsx new file mode 100644 index 00000000000..9b6dd07cd6a --- /dev/null +++ b/suite-native/receive/src/navigation/ReceiveStackNavigator.tsx @@ -0,0 +1,23 @@ +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +import { + ReceiveStackParamList, + ReceiveStackRoutes, + stackNavigationOptionsConfig, +} from '@suite-native/navigation'; + +import { ReceiveAccountsScreen } from '../screens/ReceiveAccountsScreen'; + +const ReceiveStack = createNativeStackNavigator(); + +export const ReceiveStackNavigator = () => ( + + + +); diff --git a/suite-native/receive/src/components/ReceiveAccount.tsx b/suite-native/receive/src/screens/ReceiveAccountScreen.tsx similarity index 51% rename from suite-native/receive/src/components/ReceiveAccount.tsx rename to suite-native/receive/src/screens/ReceiveAccountScreen.tsx index d6e1a66f78c..c03fba4ee4f 100644 --- a/suite-native/receive/src/components/ReceiveAccount.tsx +++ b/suite-native/receive/src/screens/ReceiveAccountScreen.tsx @@ -12,18 +12,20 @@ import { import { AccountKey, TokenAddress } from '@suite-common/wallet-types'; import { Translation } from '@suite-native/intl'; import { ConfirmOnTrezorImage } from '@suite-native/device'; +import { Screen } from '@suite-native/navigation'; import { useAccountReceiveAddress } from '../hooks/useAccountReceiveAddress'; -import { ReceiveAddressCard } from './ReceiveAddressCard'; -import { ReceiveAccountDetailsCard } from './ReceiveAccountDetailsCard'; +import { ReceiveAddressCard } from '../components/ReceiveAddressCard'; +import { ReceiveAccountDetailsCard } from '../components/ReceiveAccountDetailsCard'; import { hasReceiveAddressButtonRequest } from '../hooks/receiveSelectors'; +import { ReceiveScreenHeader } from '../components/ReceiveScreenHeader'; type AccountReceiveProps = { accountKey: AccountKey; tokenContract?: TokenAddress; }; -export const ReceiveAccount = ({ accountKey, tokenContract }: AccountReceiveProps) => { +export const ReceiveAccountScreen = ({ accountKey, tokenContract }: AccountReceiveProps) => { const dispatch = useDispatch(); const account = useSelector((state: AccountsRootState) => @@ -50,31 +52,38 @@ export const ReceiveAccount = ({ accountKey, tokenContract }: AccountReceiveProp isUnverifiedAddressRevealed && !isReceiveApproved && hasReceiveButtonRequest; return ( - - - {isAccountDetailVisible && ( - + } + footer={ + isConfirmOnTrezorReady && ( + + } /> - )} - - - - {isConfirmOnTrezorReady && ( - - } - /> - )} - + ) + } + > + + + {isAccountDetailVisible && ( + + )} + + + + ); }; diff --git a/suite-native/receive/src/screens/ReceiveAccountsScreen.tsx b/suite-native/receive/src/screens/ReceiveAccountsScreen.tsx new file mode 100644 index 00000000000..a04af0b4948 --- /dev/null +++ b/suite-native/receive/src/screens/ReceiveAccountsScreen.tsx @@ -0,0 +1,45 @@ +import { useNavigation } from '@react-navigation/native'; + +import { AccountsList, AddAccountButton, OnSelectAccount } from '@suite-native/accounts'; +import { useTranslate } from '@suite-native/intl'; +import { + ReceiveStackParamList, + ReceiveStackRoutes, + RootStackParamList, + RootStackRoutes, + Screen, + ScreenSubHeader, + StackToStackCompositeNavigationProps, +} from '@suite-native/navigation'; + +type NavigationProps = StackToStackCompositeNavigationProps< + ReceiveStackParamList, + ReceiveStackRoutes.ReceiveAccounts, + RootStackParamList +>; + +export const ReceiveAccountsScreen = () => { + const { translate } = useTranslate(); + const navigation = useNavigation(); + + const navigateToReceiveScreen: OnSelectAccount = ({ account, tokenAddress }) => + navigation.navigate(RootStackRoutes.ReceiveModal, { + accountKey: account.key, + tokenContract: tokenAddress, + closeActionType: 'back', + }); + + return ( + } + closeActionType="close" + /> + } + > + + + ); +}; diff --git a/suite-native/receive/src/screens/ReceiveModalScreen.tsx b/suite-native/receive/src/screens/ReceiveModalScreen.tsx index c8dd2f53d4a..ce2ba050341 100644 --- a/suite-native/receive/src/screens/ReceiveModalScreen.tsx +++ b/suite-native/receive/src/screens/ReceiveModalScreen.tsx @@ -1,42 +1,28 @@ -import { useSelector } from 'react-redux'; -import { useEffect } from 'react'; import { Dimensions } from 'react-native'; +import { useSelector } from 'react-redux'; import { CommonActions, RouteProp, useNavigation, useRoute } from '@react-navigation/native'; -import TrezorConnect from '@trezor/connect'; -import { BoxSkeleton, Card, HStack, Text, VStack } from '@suite-native/atoms'; +import { + AccountsRootState, + DeviceRootState, + selectDeviceAccountKeyForNetworkSymbolAndAccountTypeWithIndex, +} from '@suite-common/wallet-core'; +import { AccountsList, OnSelectAccount } from '@suite-native/accounts'; +import { BoxSkeleton, Card, VStack } from '@suite-native/atoms'; import { RootStackParamList, RootStackRoutes, Screen, - ScreenSubHeader, StackNavigationProps, - GoBackIcon, } from '@suite-native/navigation'; -import { AccountsList, OnSelectAccount } from '@suite-native/accounts'; -import { AccountKey, TokenAddress } from '@suite-common/wallet-types'; -import { Translation } from '@suite-native/intl'; -import { - AccountsRootState, - DeviceRootState, - selectAccountLabel, - selectAccountNetworkSymbol, - selectDeviceAccountKeyForNetworkSymbolAndAccountTypeWithIndex, -} from '@suite-common/wallet-core'; -import { selectAccountTokenSymbol, TokensRootState } from '@suite-native/tokens'; -import { CryptoIcon } from '@suite-native/icons'; import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; -import { ReceiveAccount } from '../components/ReceiveAccount'; +import { ReceiveAccountScreen } from './ReceiveAccountScreen'; +import { ReceiveScreenHeader } from '../components/ReceiveScreenHeader'; const SCREEN_WIDTH = Dimensions.get('window').width; -type ScreenSubHeaderContent = { - accountKey?: AccountKey; - tokenContract?: TokenAddress; -}; - const cardStyle = prepareNativeStyle(utils => ({ padding: utils.spacings.sp8, })); @@ -59,56 +45,6 @@ const LoadingReceiveAccount = () => { ); }; -const ReceiveModalScreenSubHeader = ({ accountKey, tokenContract }: ScreenSubHeaderContent) => { - const { - params: { closeActionType }, - } = useRoute>(); - const navigation = useNavigation(); - const accountLabel = useSelector((state: AccountsRootState) => - selectAccountLabel(state, accountKey), - ); - const symbol = useSelector((state: AccountsRootState) => - selectAccountNetworkSymbol(state, accountKey), - ); - const tokenSymbol = useSelector((state: TokensRootState) => - selectAccountTokenSymbol(state, accountKey, tokenContract), - ); - - useEffect(() => { - const unsubscribe = navigation.addListener('beforeRemove', () => { - // When leaving the screen, cancel the request for address on trezor device - TrezorConnect.cancel(); - }); - - return unsubscribe; - }, [navigation]); - - return ( - - - - - - {symbol && } - {accountLabel && ( - - {accountLabel} - {tokenSymbol && ` - ${tokenSymbol}`} - - )} - - - } - leftIcon={} - /> - ); -}; - export const ReceiveModalScreen = () => { const { params: { @@ -150,17 +86,17 @@ export const ReceiveModalScreen = () => { !accountKey && (routeNetworkSymbol !== undefined || routeAccountType !== undefined); const isSelecting = !isLoading && !accountKey; + if (accountKey) { + return ; + } + return ( + } > - {accountKey && } {isLoading && } {isSelecting && } diff --git a/yarn.lock b/yarn.lock index be228df49bd..dbfc67e5660 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9875,7 +9875,6 @@ __metadata: "@suite-native/alerts": "workspace:*" "@suite-native/atoms": "workspace:*" "@suite-native/config": "workspace:*" - "@suite-native/feature-flags": "workspace:*" "@suite-native/formatters": "workspace:*" "@suite-native/forms": "workspace:*" "@suite-native/icons": "workspace:*" @@ -11030,6 +11029,7 @@ __metadata: dependencies: "@mobily/ts-belt": "npm:^3.13.1" "@react-navigation/native": "npm:6.1.18" + "@react-navigation/native-stack": "npm:6.11.0" "@reduxjs/toolkit": "npm:1.9.5" "@shopify/react-native-skia": "npm:^1.5.10" "@suite-common/wallet-config": "workspace:*"