diff --git a/suite-native/app/package.json b/suite-native/app/package.json index e41fb9366ba..1eaa5c85c6e 100644 --- a/suite-native/app/package.json +++ b/suite-native/app/package.json @@ -63,6 +63,7 @@ "@suite-native/module-authorize-device": "workspace:*", "@suite-native/module-connect-popup": "workspace:*", "@suite-native/module-dev-utils": "workspace:*", + "@suite-native/module-device-onboarding": "workspace:*", "@suite-native/module-device-settings": "workspace:*", "@suite-native/module-home": "workspace:*", "@suite-native/module-onboarding": "workspace:*", diff --git a/suite-native/app/src/navigation/RootStackNavigator.tsx b/suite-native/app/src/navigation/RootStackNavigator.tsx index 9ebc8788040..7751f78e569 100644 --- a/suite-native/app/src/navigation/RootStackNavigator.tsx +++ b/suite-native/app/src/navigation/RootStackNavigator.tsx @@ -13,6 +13,7 @@ import { DeviceCompromisedModalScreen } from '@suite-native/module-authenticity- import { AuthorizeDeviceStackNavigator } from '@suite-native/module-authorize-device'; import { ConnectPopupScreen } from '@suite-native/module-connect-popup'; import { DevUtilsStackNavigator } from '@suite-native/module-dev-utils'; +import { DeviceOnboardingStackNavigator } from '@suite-native/module-device-onboarding'; import { DeviceSettingsStackNavigator } from '@suite-native/module-device-settings'; import { OnboardingStackNavigator } from '@suite-native/module-onboarding'; import { SendStackNavigator } from '@suite-native/module-send'; @@ -90,6 +91,10 @@ export const RootStackNavigator = () => { /> {/* Navigation flows that start by push from bottom animation on the first screen of its stack. */} + { // We should not redirect him away so he can read the screen content and decide what to do. // If the device is connected again, he still should stay on that screen. const isSuspiciousDeviceScreenFocused = useNavigationRouteMatch( - OnboardingStackRoutes.SuspiciousDevice, + DeviceOnboardingStackRoutes.SuspiciousDevice, ); const isConnectAndUnlockDeviceScreenFocused = useNavigationRouteMatch( AuthorizeDeviceStackRoutes.ConnectAndUnlockDevice, @@ -83,6 +83,7 @@ export const useHandleDeviceConnection = () => { const isDeviceSettingsStackFocused = lastRoute === RootStackRoutes.DeviceSettingsStack; const isSendStackFocused = lastRoute === RootStackRoutes.SendStack; const isOnboardingStackFocused = lastRoute === RootStackRoutes.OnboardingStack; + const isDeviceOnboardingStackFocused = lastRoute === RootStackRoutes.DeviceOnboardingStack; const shouldBlockSendReviewRedirect = isDeviceRemembered && isSendStackFocused; const isDeviceCompromisedModalFocused = lastRoute === RootStackRoutes.DeviceCompromisedModalScreen; @@ -95,25 +96,29 @@ export const useHandleDeviceConnection = () => { if ( isDeviceSetupSupported && isDeviceConnected && + isOnboardingFinished && !isDeviceInitialized && !isPortfolioTrackerDevice && !isBiometricsOverlayVisible && - !isOnboardingStackFocused && - !isOnboardingDeviceDisconnectedAlertDisplayed + !isDeviceOnboardingStackFocused && + !isOnboardingDeviceDisconnectedAlertDisplayed && + !isFirmwareInstallationRunning ) { - navigation.navigate(RootStackRoutes.OnboardingStack, { - screen: OnboardingStackRoutes.UninitializedDeviceLanding, + navigation.navigate(RootStackRoutes.DeviceOnboardingStack, { + screen: DeviceOnboardingStackRoutes.UninitializedDeviceLanding, }); } }, [ dispatch, isDeviceConnected, - isOnboardingStackFocused, + isOnboardingFinished, isBiometricsOverlayVisible, navigation, isDeviceInitialized, isPortfolioTrackerDevice, isDeviceSetupSupported, + isDeviceOnboardingStackFocused, + isFirmwareInstallationRunning, isOnboardingDeviceDisconnectedAlertDisplayed, ]); @@ -168,7 +173,7 @@ export const useHandleDeviceConnection = () => { // set connecting screen to be displayed again on the next device connection. useEffect(() => { if ( - (isFirmwareInstallationRunning && !isOnboardingStackFocused) || + isFirmwareInstallationRunning || !isOnboardingFinished || isConnectAndUnlockDeviceScreenFocused ) @@ -186,7 +191,7 @@ export const useHandleDeviceConnection = () => { return; } - if (isOnboardingStackFocused) { + if (isDeviceOnboardingStackFocused) { handleOnboardingDeviceDisconnection(); return; @@ -210,6 +215,7 @@ export const useHandleDeviceConnection = () => { isOnboardingStackFocused, handleOnboardingDeviceDisconnection, isConnectAndUnlockDeviceScreenFocused, + isDeviceOnboardingStackFocused, ]); // When trezor gets locked, it is necessary to display a PIN matrix for T1 so that it can be unlocked diff --git a/suite-native/device/src/hooks/useHandleOnboardingDeviceDisconnection.tsx b/suite-native/device/src/hooks/useHandleOnboardingDeviceDisconnection.tsx index 77302eb5ab3..500e2f45149 100644 --- a/suite-native/device/src/hooks/useHandleOnboardingDeviceDisconnection.tsx +++ b/suite-native/device/src/hooks/useHandleOnboardingDeviceDisconnection.tsx @@ -70,19 +70,22 @@ export const useHandleOnboardingDeviceDisconnection = () => { // We need to wait for the navigation to be completed before showing the alert. setTimeout(() => { showAlert({ - title: translate('moduleOnboarding.deviceDisconnectedAlert.title'), + title: translate('moduleDeviceOnboarding.deviceDisconnectedAlert.title'), description: translate( - 'moduleOnboarding.deviceDisconnectedAlert.description', + 'moduleDeviceOnboarding.deviceDisconnectedAlert.description', ), primaryButtonTitle: translate( - 'moduleOnboarding.deviceDisconnectedAlert.reconnectButton', + 'moduleDeviceOnboarding.deviceDisconnectedAlert.reconnectButton', ), pictogramVariant: 'critical', primaryButtonVariant: 'redBold', secondaryButtonTitle: translate('generic.buttons.cancel'), secondaryButtonVariant: 'redElevation0', onPressPrimaryButton: hideDeviceDisconnectedAlert, - onPressSecondaryButton: hideDeviceDisconnectedAlert, + onPressSecondaryButton: () => { + hideDeviceDisconnectedAlert(); + navigation.goBack(); + }, }); }, 300); } diff --git a/suite-native/device/src/index.ts b/suite-native/device/src/index.ts index 761eea5e08d..00388cb2018 100644 --- a/suite-native/device/src/index.ts +++ b/suite-native/device/src/index.ts @@ -5,6 +5,7 @@ export * from './hooks/useDetectDeviceError'; export * from './hooks/useReportDeviceConnectToAnalytics'; export * from './hooks/useReportDeviceCompromised'; export * from './hooks/useRetryFwAuthenticityChecks'; +export * from './hooks/useHandleOnboardingDeviceDisconnection'; export * from './components/ConnectDeviceAnimation'; export * from './components/ConfirmOnTrezorImage'; export * from './components/ConnectorImage'; diff --git a/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx b/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx index 0fdf40c1024..b5a5f042f9b 100644 --- a/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx +++ b/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx @@ -45,6 +45,7 @@ type FirmwareInstallationScreenContentProps = { onFirmwareInstallationSuccess: () => void; onFirmwareInstallationFailure?: () => void; isCancellationAllowed?: boolean; + isRetryAllowed?: boolean; }; // This component is shared between `module-onboarding` and `module-device-settings`. @@ -53,6 +54,7 @@ export const FirmwareInstallationScreenContent = ({ onFirmwareInstallationSuccess, onFirmwareInstallationFailure, isCancellationAllowed = true, + isRetryAllowed = true, }: FirmwareInstallationScreenContentProps) => { const dispatch = useDispatch(); const { applyStyle } = useNativeStyles(); @@ -228,9 +230,11 @@ export const FirmwareInstallationScreenContent = ({ bottom: bottomButtonOffset, })} > - + {isRetryAllowed && ( + + )} diff --git a/suite-native/firmware/src/hooks/useFirmware.tsx b/suite-native/firmware/src/hooks/useFirmware.tsx index b6123edefbb..fa13a47ef3e 100644 --- a/suite-native/firmware/src/hooks/useFirmware.tsx +++ b/suite-native/firmware/src/hooks/useFirmware.tsx @@ -15,7 +15,7 @@ import { useFirmwareAnalytics } from './useFirmwareAnalytics'; // If progress doesn't change for 1 minute const MAYBE_STUCKED_TIMEOUT = 1 * 60 * 1000; // 1 minute -export const useFirmware = (params: UseFirmwareInstallationParams) => { +export const useFirmware = (params?: UseFirmwareInstallationParams) => { const dispatch = useDispatch(); const { firmwareUpdate: firmwareUpdateCommon, diff --git a/suite-native/firmware/src/index.ts b/suite-native/firmware/src/index.ts index cb1e45baf53..a3be07c9212 100644 --- a/suite-native/firmware/src/index.ts +++ b/suite-native/firmware/src/index.ts @@ -4,3 +4,4 @@ export * from './components/FirmwareInstallationScreenContent'; export * from './components/ConfirmFirmwareUpdateScreenContent'; export * from './components/ConfirmFirmwareUpdateScreenFooter'; export * from './hooks/useIsFirmwareUpdateFeatureEnabled'; +export * from './hooks/useFirmware'; diff --git a/suite-native/intl/src/en.ts b/suite-native/intl/src/en.ts index a85118ed125..7888c0fc4a1 100644 --- a/suite-native/intl/src/en.ts +++ b/suite-native/intl/src/en.ts @@ -777,7 +777,8 @@ export const en = { notNow: 'Not now', }, }, - + }, + moduleDeviceOnboarding: { uninitializedDeviceLandingScreen: { noFirmware: { title: 'Now it’s just you\nand your crypto', diff --git a/suite-native/module-device-onboarding/package.json b/suite-native/module-device-onboarding/package.json new file mode 100644 index 00000000000..ca83bf375a3 --- /dev/null +++ b/suite-native/module-device-onboarding/package.json @@ -0,0 +1,33 @@ +{ + "name": "@suite-native/module-device-onboarding", + "version": "1.0.0", + "private": true, + "license": "See LICENSE.md in repo root", + "sideEffects": false, + "main": "src/index", + "scripts": { + "depcheck": "yarn g:depcheck", + "lint:js": "yarn g:eslint '**/*.{ts,tsx,js}'", + "type-check": "yarn g:tsc --build" + }, + "dependencies": { + "@react-navigation/core": "^6.4.10", + "@react-navigation/native": "6.1.18", + "@react-navigation/native-stack": "6.11.0", + "@reduxjs/toolkit": "2.6.0", + "@suite-native/atoms": "workspace:*", + "@suite-native/device": "workspace:*", + "@suite-native/icons": "workspace:*", + "@suite-native/intl": "workspace:*", + "@suite-native/navigation": "workspace:*", + "@trezor/device-utils": "workspace:*", + "@trezor/env-utils": "workspace:*", + "@trezor/styles": "workspace:*", + "jotai": "1.9.1", + "react": "18.2.0", + "react-native": "0.76.1", + "react-native-reanimated": "^3.16.7", + "react-native-svg": "^15.9.0", + "react-redux": "9.2.0" + } +} diff --git a/suite-native/module-onboarding/src/assets/t3b1.png b/suite-native/module-device-onboarding/src/assets/t3b1.png similarity index 100% rename from suite-native/module-onboarding/src/assets/t3b1.png rename to suite-native/module-device-onboarding/src/assets/t3b1.png diff --git a/suite-native/module-onboarding/src/assets/t3b1Seal1.png b/suite-native/module-device-onboarding/src/assets/t3b1Seal1.png similarity index 100% rename from suite-native/module-onboarding/src/assets/t3b1Seal1.png rename to suite-native/module-device-onboarding/src/assets/t3b1Seal1.png diff --git a/suite-native/module-onboarding/src/assets/t3b1Seal2.png b/suite-native/module-device-onboarding/src/assets/t3b1Seal2.png similarity index 100% rename from suite-native/module-onboarding/src/assets/t3b1Seal2.png rename to suite-native/module-device-onboarding/src/assets/t3b1Seal2.png diff --git a/suite-native/module-onboarding/src/assets/t3t1.png b/suite-native/module-device-onboarding/src/assets/t3t1.png similarity index 100% rename from suite-native/module-onboarding/src/assets/t3t1.png rename to suite-native/module-device-onboarding/src/assets/t3t1.png diff --git a/suite-native/module-onboarding/src/assets/t3t1Seal.png b/suite-native/module-device-onboarding/src/assets/t3t1Seal.png similarity index 100% rename from suite-native/module-onboarding/src/assets/t3t1Seal.png rename to suite-native/module-device-onboarding/src/assets/t3t1Seal.png diff --git a/suite-native/module-onboarding/src/components/HeaderUnderlineSvg.tsx b/suite-native/module-device-onboarding/src/components/HeaderUnderlineSvg.tsx similarity index 100% rename from suite-native/module-onboarding/src/components/HeaderUnderlineSvg.tsx rename to suite-native/module-device-onboarding/src/components/HeaderUnderlineSvg.tsx diff --git a/suite-native/module-onboarding/src/components/OnboardingScreenWithExitButton.tsx b/suite-native/module-device-onboarding/src/components/OnboardingScreenWithExitButton.tsx similarity index 69% rename from suite-native/module-onboarding/src/components/OnboardingScreenWithExitButton.tsx rename to suite-native/module-device-onboarding/src/components/OnboardingScreenWithExitButton.tsx index 32178020e96..1659cfcc4f0 100644 --- a/suite-native/module-onboarding/src/components/OnboardingScreenWithExitButton.tsx +++ b/suite-native/module-device-onboarding/src/components/OnboardingScreenWithExitButton.tsx @@ -7,35 +7,45 @@ import { useSetAtom } from 'jotai'; import { deviceActions, selectSelectedDevice } from '@suite-common/wallet-core'; import { useAlert } from '@suite-native/alerts'; import { wasDeviceDisconnectedByUserActionAtom } from '@suite-native/device'; +import { useFirmware } from '@suite-native/firmware'; import { useTranslate } from '@suite-native/intl'; import { Screen, ScreenHeader, ScreenProps } from '@suite-native/navigation'; -const OnboardingExitButtonScreenHeader = () => { +const DeviceOnboardingExitButtonScreenHeader = () => { const navigation = useNavigation(); const { showAlert } = useAlert(); const { translate } = useTranslate(); const dispatch = useDispatch(); const selectedDevice = useSelector(selectSelectedDevice); const setWasDeviceDisconnectedByUserAction = useSetAtom(wasDeviceDisconnectedByUserActionAtom); + const { setIsFirmwareInstallationRunning } = useFirmware(); const handleExitButtonPress = useCallback(() => { showAlert({ - title: translate('moduleOnboarding.cancelOnboardingAlert.title'), - description: translate('moduleOnboarding.cancelOnboardingAlert.description'), + title: translate('moduleDeviceOnboarding.cancelOnboardingAlert.title'), + description: translate('moduleDeviceOnboarding.cancelOnboardingAlert.description'), primaryButtonTitle: translate('generic.buttons.cancel'), primaryButtonVariant: 'redBold', secondaryButtonTitle: translate( - 'moduleOnboarding.cancelOnboardingAlert.continueButton', + 'moduleDeviceOnboarding.cancelOnboardingAlert.continueButton', ), secondaryButtonVariant: 'redElevation0', onPressPrimaryButton: () => { if (selectedDevice) { + setIsFirmwareInstallationRunning(false); setWasDeviceDisconnectedByUserAction(true); dispatch(deviceActions.deviceDisconnect(selectedDevice)); } }, }); - }, [dispatch, selectedDevice, setWasDeviceDisconnectedByUserAction, translate, showAlert]); + }, [ + dispatch, + selectedDevice, + setWasDeviceDisconnectedByUserAction, + setIsFirmwareInstallationRunning, + translate, + showAlert, + ]); useEffect(() => { // Override default navigation GO_BACK action to align it with the exit button behavior. @@ -52,8 +62,8 @@ const OnboardingExitButtonScreenHeader = () => { return ; }; -export const OnboardingScreenWithExitButton = ({ children, ...screenProps }: ScreenProps) => ( - } {...screenProps}> +export const DeviceOnboardingScreenWithExitButton = ({ children, ...screenProps }: ScreenProps) => ( + } {...screenProps}> {children} ); diff --git a/suite-native/module-onboarding/src/components/SecurityCheckStepCard.tsx b/suite-native/module-device-onboarding/src/components/SecurityCheckStepCard.tsx similarity index 92% rename from suite-native/module-onboarding/src/components/SecurityCheckStepCard.tsx rename to suite-native/module-device-onboarding/src/components/SecurityCheckStepCard.tsx index d53fab0b242..7d43e090a6d 100644 --- a/suite-native/module-onboarding/src/components/SecurityCheckStepCard.tsx +++ b/suite-native/module-device-onboarding/src/components/SecurityCheckStepCard.tsx @@ -21,9 +21,9 @@ import { import { Icon, IconName } from '@suite-native/icons'; import { Translation } from '@suite-native/intl'; import { + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, DeviceSuspicionCause, - OnboardingStackParamList, - OnboardingStackRoutes, StackNavigationProps, } from '@suite-native/navigation'; import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; @@ -40,8 +40,8 @@ type SecurityCheckStepCardProps = { }; type NavigationProps = StackNavigationProps< - OnboardingStackParamList, - OnboardingStackRoutes.SecurityCheck + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.SecurityCheck >; const ANIMATION_DURATION = 300; @@ -71,7 +71,7 @@ export const SecurityCheckStepCard = ({ const headerColor: Color = isChecked ? 'textPrimaryDefault' : 'textSubdued'; const navigateToSuspiciousDeviceScreen = () => { - navigation.navigate(OnboardingStackRoutes.SuspiciousDevice, { + navigation.navigate(DeviceOnboardingStackRoutes.SuspiciousDevice, { suspicionCause, }); }; @@ -128,7 +128,7 @@ export const SecurityCheckStepCard = ({ colorScheme="tertiaryElevation0" onPress={navigateToSuspiciousDeviceScreen} > - + diff --git a/suite-native/module-onboarding/src/components/SecuritySealDescription.tsx b/suite-native/module-device-onboarding/src/components/SecuritySealDescription.tsx similarity index 93% rename from suite-native/module-onboarding/src/components/SecuritySealDescription.tsx rename to suite-native/module-device-onboarding/src/components/SecuritySealDescription.tsx index 5cd825d6eaa..06cef89dc2a 100644 --- a/suite-native/module-onboarding/src/components/SecuritySealDescription.tsx +++ b/suite-native/module-device-onboarding/src/components/SecuritySealDescription.tsx @@ -33,7 +33,7 @@ export const SecuritySealDescription = () => { <> ( { - + - + - + diff --git a/suite-native/module-onboarding/src/components/SecuritySealImages.tsx b/suite-native/module-device-onboarding/src/components/SecuritySealImages.tsx similarity index 94% rename from suite-native/module-onboarding/src/components/SecuritySealImages.tsx rename to suite-native/module-device-onboarding/src/components/SecuritySealImages.tsx index 10b1aab968b..5e86300ac01 100644 --- a/suite-native/module-onboarding/src/components/SecuritySealImages.tsx +++ b/suite-native/module-device-onboarding/src/components/SecuritySealImages.tsx @@ -51,7 +51,7 @@ export const SecuritySealImages = () => { textVariant="hint" contentColor="textDefault" title={ - + } /> diff --git a/suite-native/module-device-onboarding/src/index.ts b/suite-native/module-device-onboarding/src/index.ts new file mode 100644 index 00000000000..695c3c49885 --- /dev/null +++ b/suite-native/module-device-onboarding/src/index.ts @@ -0,0 +1 @@ +export * from './navigation/DeviceOnboardingStackNavigator'; diff --git a/suite-native/module-device-onboarding/src/navigation/DeviceOnboardingStackNavigator.tsx b/suite-native/module-device-onboarding/src/navigation/DeviceOnboardingStackNavigator.tsx new file mode 100644 index 00000000000..9015fc24065 --- /dev/null +++ b/suite-native/module-device-onboarding/src/navigation/DeviceOnboardingStackNavigator.tsx @@ -0,0 +1,43 @@ +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +import { + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, + stackNavigationOptionsConfig, +} from '@suite-native/navigation'; + +import { ConfirmFirmwareUpdateScreen } from '../screens/ConfirmFirmwareUpdateScreen'; +import { FirmwareInstallationScreen } from '../screens/FirmwareInstallationScreen'; +import { SecurityCheckScreen } from '../screens/SecurityCheckScreen'; +import { SuspiciousDeviceScreen } from '../screens/SuspiciousDeviceScreen'; +import { UninitializedDeviceLandingScreen } from '../screens/UninitializedDeviceLandingScreen'; + +export const DeviceOnboardingStack = createNativeStackNavigator(); + +export const DeviceOnboardingStackNavigator = () => ( + + + + + + + +); diff --git a/suite-native/module-device-onboarding/src/redux.d.ts b/suite-native/module-device-onboarding/src/redux.d.ts new file mode 100644 index 00000000000..df9a0c3f969 --- /dev/null +++ b/suite-native/module-device-onboarding/src/redux.d.ts @@ -0,0 +1,7 @@ +import { AsyncThunkAction } from '@reduxjs/toolkit'; + +declare module 'redux' { + export interface Dispatch { + >(thunk: TThunk): ReturnType; + } +} diff --git a/suite-native/module-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx b/suite-native/module-device-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx similarity index 82% rename from suite-native/module-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx rename to suite-native/module-device-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx index f5ac71cbe23..56d2b299d6e 100644 --- a/suite-native/module-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/ConfirmFirmwareUpdateScreen.tsx @@ -10,17 +10,17 @@ import { ConfirmFirmwareUpdateScreenFooter, } from '@suite-native/firmware'; import { - OnboardingStackParamList, - OnboardingStackRoutes, + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, StackNavigationProps, } from '@suite-native/navigation'; import { useToast } from '@suite-native/toasts'; -import { OnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; +import { DeviceOnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; type NavigationProp = StackNavigationProps< - OnboardingStackParamList, - OnboardingStackRoutes.ConfirmFirmwareUpdate + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.ConfirmFirmwareUpdate >; export const ConfirmFirmwareUpdateScreen = () => { @@ -41,7 +41,7 @@ export const ConfirmFirmwareUpdateScreen = () => { ); const handleUpdateConfirmation = () => { - navigation.navigate(OnboardingStackRoutes.FirmwareInstallation); + navigation.navigate(DeviceOnboardingStackRoutes.FirmwareInstallation); }; const handleSkipUpdate = () => { @@ -52,7 +52,7 @@ export const ConfirmFirmwareUpdateScreen = () => { }; return ( - { } > - + ); }; diff --git a/suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx b/suite-native/module-device-onboarding/src/screens/FirmwareInstallationScreen.tsx similarity index 73% rename from suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx rename to suite-native/module-device-onboarding/src/screens/FirmwareInstallationScreen.tsx index 3a2eec38794..7b5a4e8307c 100644 --- a/suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/FirmwareInstallationScreen.tsx @@ -1,7 +1,7 @@ import { FirmwareInstallationScreenContent } from '@suite-native/firmware'; import { useToast } from '@suite-native/toasts'; -import { OnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; +import { DeviceOnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; export const FirmwareInstallationScreen = () => { const { showToast } = useToast(); @@ -13,11 +13,12 @@ export const FirmwareInstallationScreen = () => { }; return ( - + - + ); }; diff --git a/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx b/suite-native/module-device-onboarding/src/screens/SecurityCheckScreen.tsx similarity index 71% rename from suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx rename to suite-native/module-device-onboarding/src/screens/SecurityCheckScreen.tsx index 7d860b3e532..ba24c953a0d 100644 --- a/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/SecurityCheckScreen.tsx @@ -5,24 +5,24 @@ import { IconName } from '@suite-native/icons'; import { Translation } from '@suite-native/intl'; import { Link } from '@suite-native/link'; import { + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, DeviceSuspicionCause, - OnboardingStackParamList, - OnboardingStackRoutes, StackProps, } from '@suite-native/navigation'; import { TREZOR_RESELLERS_URL } from '@trezor/urls'; -import { OnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; +import { DeviceOnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; import { SecurityCheckStepCard } from '../components/SecurityCheckStepCard'; import { SecuritySealDescription } from '../components/SecuritySealDescription'; const stepToContentMap = { 1: { - header: , + header: , suspicionCause: 'untrustedReseller', description: ( ( , + header: , description: , icon: 'selectionSlash', suspicionCause: 'securitySeal', }, 3: { - header: , - description: , + header: , + description: ( + + ), icon: 'package', suspicionCause: 'packaging', }, @@ -63,7 +65,7 @@ const stepToContentMap = { export const SecurityCheckScreen = ({ navigation, -}: StackProps) => { +}: StackProps) => { const [currentStep, setCurrentStep] = useState(1); const handlePressConfirmButton = () => { @@ -73,18 +75,20 @@ export const SecurityCheckScreen = ({ return; } - navigation.navigate(OnboardingStackRoutes.FirmwareInstallation); + navigation.navigate(DeviceOnboardingStackRoutes.FirmwareInstallation); }; return ( - + } + title={ + + } subtitle={ - + } /> @@ -103,6 +107,6 @@ export const SecurityCheckScreen = ({ - + ); }; diff --git a/suite-native/module-onboarding/src/screens/SuspiciousDeviceScreen.tsx b/suite-native/module-device-onboarding/src/screens/SuspiciousDeviceScreen.tsx similarity index 81% rename from suite-native/module-onboarding/src/screens/SuspiciousDeviceScreen.tsx rename to suite-native/module-device-onboarding/src/screens/SuspiciousDeviceScreen.tsx index faecef92f17..f7bd62643aa 100644 --- a/suite-native/module-onboarding/src/screens/SuspiciousDeviceScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/SuspiciousDeviceScreen.tsx @@ -6,10 +6,10 @@ import { Translation } from '@suite-native/intl'; import { useOpenLink } from '@suite-native/link'; import { AppTabsRoutes, + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, DeviceSuspicionCause, HomeStackRoutes, - OnboardingStackParamList, - OnboardingStackRoutes, RootStackParamList, RootStackRoutes, Screen, @@ -35,8 +35,8 @@ export const SuspiciousDeviceScreen = ({ route, navigation, }: StackToStackCompositeScreenProps< - OnboardingStackParamList, - OnboardingStackRoutes.SuspiciousDevice, + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.SuspiciousDevice, RootStackParamList >) => { const { suspicionCause } = route.params; @@ -71,9 +71,11 @@ export const SuspiciousDeviceScreen = ({ } + title={ + + } subtitle={ - + } /> @@ -83,7 +85,7 @@ export const SuspiciousDeviceScreen = ({ textVariant="highlight" icon="plugs" > - + - + - + @@ -108,7 +110,7 @@ export const SuspiciousDeviceScreen = ({ colorScheme="yellowBold" onPress={handleContactSupportButtonPress} > - + diff --git a/suite-native/module-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx b/suite-native/module-device-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx similarity index 74% rename from suite-native/module-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx rename to suite-native/module-device-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx index 753708b4b07..2d2bd4ad031 100644 --- a/suite-native/module-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/UninitializedDeviceLandingScreen.tsx @@ -5,8 +5,8 @@ import { Box, Button, Image, Text, TextButton, TitleHeader, VStack } from '@suit import { SetupSupportingDeviceModel } from '@suite-native/device'; import { Translation } from '@suite-native/intl'; import { - OnboardingStackParamList, - OnboardingStackRoutes, + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes, StackProps, } from '@suite-native/navigation'; import { DeviceModelInternal } from '@trezor/device-utils'; @@ -14,7 +14,7 @@ import { getScreenHeight } from '@trezor/env-utils'; import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; import { HeaderUnderlineSvg } from '../components/HeaderUnderlineSvg'; -import { OnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; +import { DeviceOnboardingScreenWithExitButton } from '../components/OnboardingScreenWithExitButton'; const trezorImageStyle = prepareNativeStyle<{ hasDeviceFirmwareInstalled: boolean }>( (_, { hasDeviceFirmwareInstalled }) => ({ @@ -45,17 +45,17 @@ const UninitializedDeviceLandingScreenContent = () => { {hasDeviceFirmwareInstalled ? ( + } titleVariant="titleMedium" subtitle={ - + } /> ) : ( - + @@ -73,31 +73,34 @@ const UninitializedDeviceLandingScreenContent = () => { export const UninitializedDeviceLandingScreen = ({ navigation, -}: StackProps) => { +}: StackProps< + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.UninitializedDeviceLanding +>) => { const hasDeviceFirmwareInstalled = useSelector(selectHasDeviceFirmwareInstalled); const handleConfirmButtonPress = () => { if (hasDeviceFirmwareInstalled) { - navigation.navigate(OnboardingStackRoutes.ConfirmFirmwareUpdate); + navigation.navigate(DeviceOnboardingStackRoutes.ConfirmFirmwareUpdate); } else { - navigation.navigate(OnboardingStackRoutes.SecurityCheck); + navigation.navigate(DeviceOnboardingStackRoutes.SecurityCheck); } }; const handleNeverUsedThisDeviceButtonPress = () => { - navigation.navigate(OnboardingStackRoutes.SuspiciousDevice, { + navigation.navigate(DeviceOnboardingStackRoutes.SuspiciousDevice, { suspicionCause: 'firmwareAlreadyInstalled', }); }; const handleDeviceLooksDifferentButtonPress = () => { - navigation.navigate(OnboardingStackRoutes.SuspiciousDevice, { + navigation.navigate(DeviceOnboardingStackRoutes.SuspiciousDevice, { suspicionCause: 'deviceLooksDifferent', }); }; return ( - + @@ -106,7 +109,7 @@ export const UninitializedDeviceLandingScreen = ({ onPress={handleDeviceLooksDifferentButtonPress} testID="@onboarding/UninitializedDeviceLandingScreen/deviceLooksDifferentBtn" > - + @@ -114,7 +117,7 @@ export const UninitializedDeviceLandingScreen = ({ onPress={handleConfirmButtonPress} testID="@onboarding/UninitializedDeviceLandingScreen/confirmBtn" > - + {hasDeviceFirmwareInstalled && ( )} - + ); }; diff --git a/suite-native/module-device-onboarding/tsconfig.json b/suite-native/module-device-onboarding/tsconfig.json new file mode 100644 index 00000000000..eb56d63fe7c --- /dev/null +++ b/suite-native/module-device-onboarding/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "outDir": "libDev" }, + "references": [ + { "path": "../atoms" }, + { "path": "../device" }, + { "path": "../icons" }, + { "path": "../intl" }, + { "path": "../navigation" }, + { "path": "../../packages/device-utils" }, + { "path": "../../packages/env-utils" }, + { "path": "../../packages/styles" } + ] +} diff --git a/suite-native/module-onboarding/package.json b/suite-native/module-onboarding/package.json index aca7ff5aacf..af8b2069e51 100644 --- a/suite-native/module-onboarding/package.json +++ b/suite-native/module-onboarding/package.json @@ -15,20 +15,16 @@ "@react-navigation/native-stack": "6.11.0", "@reduxjs/toolkit": "2.6.0", "@suite-native/atoms": "workspace:*", - "@suite-native/device": "workspace:*", "@suite-native/icons": "workspace:*", "@suite-native/intl": "workspace:*", "@suite-native/navigation": "workspace:*", - "@trezor/device-utils": "workspace:*", "@trezor/env-utils": "workspace:*", "@trezor/styles": "workspace:*", "@trezor/utils": "workspace:*", "expo-linear-gradient": "^14.0.1", - "jotai": "1.9.1", "react": "18.2.0", "react-native": "0.76.1", "react-native-reanimated": "^3.16.7", - "react-native-svg": "^15.9.0", "react-redux": "9.2.0" } } diff --git a/suite-native/module-onboarding/src/components/E2ESkipOnboardingButton.e2e.tsx b/suite-native/module-onboarding/src/components/E2ESkipOnboardingButton.e2e.tsx index 28c1ab0fe85..2aa3fc6992e 100644 --- a/suite-native/module-onboarding/src/components/E2ESkipOnboardingButton.e2e.tsx +++ b/suite-native/module-onboarding/src/components/E2ESkipOnboardingButton.e2e.tsx @@ -12,6 +12,7 @@ export const E2ESkipOnboardingButton = () => { const navigation = useNavigation(); const redirectToHome = () => { + dispatch(setIsOnboardingFinished()); navigation.dispatch( CommonActions.reset({ index: 0, @@ -25,10 +26,6 @@ export const E2ESkipOnboardingButton = () => { ], }), ); - - // Timeout is needed to ensure that navigation event already finished before changing the redux state. - // Situation when app was still focused on the onboarding screen but the state was already change made e2e tests flaky. - setTimeout(() => dispatch(setIsOnboardingFinished()), 500); }; return ( diff --git a/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx b/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx index edc978afe0e..a0ea19adcdc 100644 --- a/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx +++ b/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx @@ -8,11 +8,6 @@ import { import { AnalyticsConsentScreen } from '../screens/AnalyticsConsentScreen'; import { BiometricsScreen } from '../screens/BiometricsScreen'; -import { ConfirmFirmwareUpdateScreen } from '../screens/ConfirmFirmwareUpdateScreen'; -import { FirmwareInstallationScreen } from '../screens/FirmwareInstallationScreen'; -import { SecurityCheckScreen } from '../screens/SecurityCheckScreen'; -import { SuspiciousDeviceScreen } from '../screens/SuspiciousDeviceScreen'; -import { UninitializedDeviceLandingScreen } from '../screens/UninitializedDeviceLandingScreen'; import { WelcomeScreen } from '../screens/WelcomeScreen'; export const OnboardingStack = createNativeStackNavigator(); @@ -31,25 +26,5 @@ export const OnboardingStackNavigator = () => ( name={OnboardingStackRoutes.Biometrics} component={BiometricsScreen} /> - - - - - ); diff --git a/suite-native/module-onboarding/src/screens/BiometricsScreen.tsx b/suite-native/module-onboarding/src/screens/BiometricsScreen.tsx index 613cf42a89c..7b7f09b27d6 100644 --- a/suite-native/module-onboarding/src/screens/BiometricsScreen.tsx +++ b/suite-native/module-onboarding/src/screens/BiometricsScreen.tsx @@ -46,6 +46,8 @@ export const BiometricsScreen = ({ }; const exitOnboardingFlow = () => { + dispatch(setIsOnboardingFinished()); + navigation.dispatch( CommonActions.reset({ index: 0, @@ -59,14 +61,6 @@ export const BiometricsScreen = ({ ], }), ); - - // FIXME: Hotfix temporary solution. - // Timeout is needed to ensure that navigation event already finishes before changing the redux state. - // Situation when the onboarding screen was still focused but the state was already changed made `useHandleDeviceConnection` - // to display the device disconnected alert even if it should not be displayed. - setTimeout(() => { - dispatch(setIsOnboardingFinished()); - }, 500); }; const handleEnableButtonPress = async () => { diff --git a/suite-native/module-onboarding/tsconfig.json b/suite-native/module-onboarding/tsconfig.json index 4843b7ffb2a..a7e4c19261f 100644 --- a/suite-native/module-onboarding/tsconfig.json +++ b/suite-native/module-onboarding/tsconfig.json @@ -3,11 +3,9 @@ "compilerOptions": { "outDir": "libDev" }, "references": [ { "path": "../atoms" }, - { "path": "../device" }, { "path": "../icons" }, { "path": "../intl" }, { "path": "../navigation" }, - { "path": "../../packages/device-utils" }, { "path": "../../packages/env-utils" }, { "path": "../../packages/styles" }, { "path": "../../packages/utils" } diff --git a/suite-native/navigation/src/navigators.ts b/suite-native/navigation/src/navigators.ts index be5e8361b31..96009289a1d 100644 --- a/suite-native/navigation/src/navigators.ts +++ b/suite-native/navigation/src/navigators.ts @@ -19,6 +19,7 @@ import { AuthorizeDeviceStackRoutes, DevUtilsStackRoutes, DeviceAuthenticityStackRoutes, + DeviceOnboardingStackRoutes, DevicePinProtectionStackRoutes, DeviceStackRoutes, HomeStackRoutes, @@ -118,13 +119,16 @@ export type OnboardingStackParamList = { [OnboardingStackRoutes.Welcome]: undefined; [OnboardingStackRoutes.AnalyticsConsent]: undefined; [OnboardingStackRoutes.Biometrics]: undefined; - [OnboardingStackRoutes.UninitializedDeviceLanding]: undefined; - [OnboardingStackRoutes.SuspiciousDevice]: { +}; + +export type DeviceOnboardingStackParamList = { + [DeviceOnboardingStackRoutes.UninitializedDeviceLanding]: undefined; + [DeviceOnboardingStackRoutes.SuspiciousDevice]: { suspicionCause: DeviceSuspicionCause; }; - [OnboardingStackRoutes.SecurityCheck]: undefined; - [OnboardingStackRoutes.FirmwareInstallation]: undefined; - [OnboardingStackRoutes.ConfirmFirmwareUpdate]: undefined; + [DeviceOnboardingStackRoutes.SecurityCheck]: undefined; + [DeviceOnboardingStackRoutes.FirmwareInstallation]: undefined; + [DeviceOnboardingStackRoutes.ConfirmFirmwareUpdate]: undefined; }; export type AccountsImportStackParamList = { @@ -207,6 +211,7 @@ export type AuthorizeDeviceStackParamList = { export type RootStackParamList = { [RootStackRoutes.AppTabs]: NavigatorScreenParams; [RootStackRoutes.OnboardingStack]: NavigatorScreenParams; + [RootStackRoutes.DeviceOnboardingStack]: NavigatorScreenParams; [RootStackRoutes.AuthorizeDeviceStack]: NavigatorScreenParams; [RootStackRoutes.AccountsImport]: NavigatorScreenParams; [RootStackRoutes.AccountSettings]: { accountKey: AccountKey }; diff --git a/suite-native/navigation/src/routes.ts b/suite-native/navigation/src/routes.ts index b33c7913abe..38ccffbec9e 100644 --- a/suite-native/navigation/src/routes.ts +++ b/suite-native/navigation/src/routes.ts @@ -2,6 +2,7 @@ export enum RootStackRoutes { AppTabs = 'AppTabs', LegacyOnboarding = 'LegacyOnboarding', OnboardingStack = 'OnboardingStack', + DeviceOnboardingStack = 'DeviceOnboardingStack', AccountsImport = 'AccountsImport', AuthorizeDeviceStack = 'AuthorizeDeviceStack', AccountDetail = 'AccountDetail', @@ -30,6 +31,9 @@ export enum OnboardingStackRoutes { Welcome = 'Welcome', AnalyticsConsent = 'AnalyticsConsent', Biometrics = 'Biometrics', +} + +export enum DeviceOnboardingStackRoutes { UninitializedDeviceLanding = 'UninitializedDeviceLanding', SuspiciousDevice = 'SuspiciousDevice', SecurityCheck = 'SecurityCheck', diff --git a/yarn.lock b/yarn.lock index 6061e24df46..d6899b3e6da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9838,6 +9838,7 @@ __metadata: "@suite-native/module-authorize-device": "workspace:*" "@suite-native/module-connect-popup": "workspace:*" "@suite-native/module-dev-utils": "workspace:*" + "@suite-native/module-device-onboarding": "workspace:*" "@suite-native/module-device-settings": "workspace:*" "@suite-native/module-home": "workspace:*" "@suite-native/module-onboarding": "workspace:*" @@ -10596,6 +10597,31 @@ __metadata: languageName: unknown linkType: soft +"@suite-native/module-device-onboarding@workspace:*, @suite-native/module-device-onboarding@workspace:suite-native/module-device-onboarding": + version: 0.0.0-use.local + resolution: "@suite-native/module-device-onboarding@workspace:suite-native/module-device-onboarding" + dependencies: + "@react-navigation/core": "npm:^6.4.10" + "@react-navigation/native": "npm:6.1.18" + "@react-navigation/native-stack": "npm:6.11.0" + "@reduxjs/toolkit": "npm:2.6.0" + "@suite-native/atoms": "workspace:*" + "@suite-native/device": "workspace:*" + "@suite-native/icons": "workspace:*" + "@suite-native/intl": "workspace:*" + "@suite-native/navigation": "workspace:*" + "@trezor/device-utils": "workspace:*" + "@trezor/env-utils": "workspace:*" + "@trezor/styles": "workspace:*" + jotai: "npm:1.9.1" + react: "npm:18.2.0" + react-native: "npm:0.76.1" + react-native-reanimated: "npm:^3.16.7" + react-native-svg: "npm:^15.9.0" + react-redux: "npm:9.2.0" + languageName: unknown + linkType: soft + "@suite-native/module-device-settings@workspace:*, @suite-native/module-device-settings@workspace:suite-native/module-device-settings": version: 0.0.0-use.local resolution: "@suite-native/module-device-settings@workspace:suite-native/module-device-settings" @@ -10671,20 +10697,16 @@ __metadata: "@react-navigation/native-stack": "npm:6.11.0" "@reduxjs/toolkit": "npm:2.6.0" "@suite-native/atoms": "workspace:*" - "@suite-native/device": "workspace:*" "@suite-native/icons": "workspace:*" "@suite-native/intl": "workspace:*" "@suite-native/navigation": "workspace:*" - "@trezor/device-utils": "workspace:*" "@trezor/env-utils": "workspace:*" "@trezor/styles": "workspace:*" "@trezor/utils": "workspace:*" expo-linear-gradient: "npm:^14.0.1" - jotai: "npm:1.9.1" react: "npm:18.2.0" react-native: "npm:0.76.1" react-native-reanimated: "npm:^3.16.7" - react-native-svg: "npm:^15.9.0" react-redux: "npm:9.2.0" languageName: unknown linkType: soft