From 28a92010f56bcbf8ae659414163b4b8c952798dc Mon Sep 17 00:00:00 2001 From: Petr Knetl Date: Wed, 19 Feb 2025 14:06:14 +0100 Subject: [PATCH 1/2] refactor(suite-native): fw installation made reusable for other flows --- .../FirmwareInstallationScreenContent.tsx} | 88 +++++++++---------- suite-native/firmware/src/index.ts | 2 +- .../DeviceSettingsStackNavigator.tsx | 6 +- .../screens/FirmwareInstallationScreen.tsx | 41 +++++++++ .../FirmwareUpdateScreen.tsx | 2 +- suite-native/navigation/src/navigators.ts | 3 +- suite-native/navigation/src/routes.ts | 3 +- 7 files changed, 94 insertions(+), 51 deletions(-) rename suite-native/firmware/src/{screens/FirmwareUpdateInProgressScreen.tsx => components/FirmwareInstallationScreenContent.tsx} (81%) create mode 100644 suite-native/module-device-settings/src/screens/FirmwareInstallationScreen.tsx diff --git a/suite-native/firmware/src/screens/FirmwareUpdateInProgressScreen.tsx b/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx similarity index 81% rename from suite-native/firmware/src/screens/FirmwareUpdateInProgressScreen.tsx rename to suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx index f2e94b73897..3d0c71077c7 100644 --- a/suite-native/firmware/src/screens/FirmwareUpdateInProgressScreen.tsx +++ b/suite-native/firmware/src/components/FirmwareInstallationScreenContent.tsx @@ -17,12 +17,6 @@ import { ConfirmOnTrezorImage, setDeviceForceRememberedThunk } from '@suite-nati import { requestPrioritizedDeviceAccess } from '@suite-native/device-mutex'; import { Translation } from '@suite-native/intl'; import { SUITE_LITE_SUPPORT_URL, useOpenLink } from '@suite-native/link'; -import { - DeviceSettingsStackParamList, - DeviceStackRoutes, - Screen, - StackNavigationProps, -} from '@suite-native/navigation'; import TrezorConnect from '@trezor/connect'; import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; @@ -34,11 +28,6 @@ import { import { useFirmware } from '../hooks/useFirmware'; import { useFirmwareAnalytics } from '../hooks/useFirmwareAnalytics'; -type NavigationProp = StackNavigationProps< - DeviceSettingsStackParamList, - DeviceStackRoutes.FirmwareUpdateInProgress ->; - const bottomButtonsContainerStyle = prepareNativeStyle<{ bottom: number }>((utils, { bottom }) => ({ position: 'absolute', left: utils.spacings.sp16, @@ -52,11 +41,21 @@ const cancelButtonStyle = prepareNativeStyle(utils => ({ top: utils.spacings.sp8, })); -export const FirmwareUpdateInProgressScreen = () => { +type FirmwareInstallationScreenContentProps = { + onFirmwareInstallationSuccess: () => void; + onFirmwareInstallationFailure: () => void; +}; + +// This component is shared between `module-onboarding` and `module-device-settings`. +// Avoid doing anything module specific in this file!!! +export const FirmwareInstallationScreenContent = ({ + onFirmwareInstallationSuccess, + onFirmwareInstallationFailure, +}: FirmwareInstallationScreenContentProps) => { const dispatch = useDispatch(); const { applyStyle } = useNativeStyles(); - const navigation = useNavigation(); - const [isMayBeStuckedBottomSheetOpened, setIsMayBeStuckedBottomSheetOpened] = + const navigation = useNavigation(); + const [isMayBeStuckBottomSheetOpened, setIsMayBeStuckBottomSheetOpened] = useState(false); const { bottom: bottomSafeAreaInset } = useSafeAreaInsets(); const { @@ -93,19 +92,14 @@ export const FirmwareUpdateInProgressScreen = () => { }; }, [dispatch, resetReducer]); - const handleFirmwareUpdateFinished = useCallback(() => { - requestPrioritizedDeviceAccess({ + const handleFirmwareUpdateFinished = useCallback(async () => { + await requestPrioritizedDeviceAccess({ deviceCallback: () => dispatch(authorizeDeviceThunk()), }); - const initialRoute = navigation.getState().routes.at(0)?.name; - if (initialRoute) { - navigation.navigate(initialRoute); - } else { - // This cause should not happen, but just to be safe - navigation.popToTop(); - } - }, [dispatch, navigation]); + setIsFirmwareInstallationRunning(false); + onFirmwareInstallationSuccess(); + }, [dispatch, onFirmwareInstallationSuccess, setIsFirmwareInstallationRunning]); const handleCancel = useCallback(() => { navigation.goBack(); @@ -113,7 +107,6 @@ export const FirmwareUpdateInProgressScreen = () => { const startFirmwareUpdate = useCallback(async () => { setIsFirmwareInstallationRunning(true); - const result = await firmwareUpdate(); if (!result) { @@ -128,7 +121,7 @@ export const FirmwareUpdateInProgressScreen = () => { result.payload?.code === 'Failure_ActionCancelled' ) { handleAnalyticsReportCancelled(); - navigation.navigate(DeviceStackRoutes.FirmwareUpdate); + onFirmwareInstallationFailure(); return; } @@ -139,17 +132,9 @@ export const FirmwareUpdateInProgressScreen = () => { } handleAnalyticsReportFinished(); - - // wait few seconds to animation to finish and let user orientate little bit - setTimeout(() => { - // setting this to false will trigger standart device connection flow - setIsFirmwareInstallationRunning(false); - handleFirmwareUpdateFinished(); - }, 5000); }, [ setIsFirmwareInstallationRunning, - navigation, - handleFirmwareUpdateFinished, + onFirmwareInstallationFailure, firmwareUpdate, handleAnalyticsReportFinished, handleAnalyticsReportCancelled, @@ -163,12 +148,12 @@ export const FirmwareUpdateInProgressScreen = () => { startFirmwareUpdate(); }, [startFirmwareUpdate, resetReducer, handleAnalyticsReportStarted]); - const openMayBeStuckedBottomSheet = useCallback(() => { - setIsMayBeStuckedBottomSheetOpened(true); + const openMayBeStuckBottomSheet = useCallback(() => { + setIsMayBeStuckBottomSheetOpened(true); }, []); - const closeMayBeStuckedBottomSheet = useCallback(() => { - setIsMayBeStuckedBottomSheetOpened(false); + const closeMayBeStuckBottomSheet = useCallback(() => { + setIsMayBeStuckBottomSheetOpened(false); }, []); const handleContactSupport = useCallback(() => { @@ -186,6 +171,7 @@ export const FirmwareUpdateInProgressScreen = () => { }, [startFirmwareUpdate, handleAnalyticsReportStarted]); const isError = status === 'error'; + const isDone = status === 'done'; const indicatorStatus: UpdateProgressIndicatorStatus = useMemo(() => { const isStarting = (status === 'started' && operation === null) || status === 'initial'; @@ -204,7 +190,7 @@ export const FirmwareUpdateInProgressScreen = () => { const bottomButtonOffset = showConfirmOnDevice ? 180 : bottomSafeAreaInset + 12; return ( - + <> {isError && ( { bottom: bottomButtonOffset, })} > - )} + {isDone && ( + + + + )} {showConfirmOnDevice && ( { /> )} - + ); }; diff --git a/suite-native/firmware/src/index.ts b/suite-native/firmware/src/index.ts index e92087d2abe..99996122cca 100644 --- a/suite-native/firmware/src/index.ts +++ b/suite-native/firmware/src/index.ts @@ -1,4 +1,4 @@ export * from './nativeFirmwareSlice'; export * from './components/UpdateProgressIndicatorDemo'; -export * from './screens/FirmwareUpdateInProgressScreen'; +export * from './components/FirmwareInstallationScreenContent'; export * from './hooks/useIsFirmwareUpdateFeatureEnabled'; diff --git a/suite-native/module-device-settings/src/navigation/DeviceSettingsStackNavigator.tsx b/suite-native/module-device-settings/src/navigation/DeviceSettingsStackNavigator.tsx index 76fce5fdeef..9a8cc07a727 100644 --- a/suite-native/module-device-settings/src/navigation/DeviceSettingsStackNavigator.tsx +++ b/suite-native/module-device-settings/src/navigation/DeviceSettingsStackNavigator.tsx @@ -1,6 +1,5 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { FirmwareUpdateInProgressScreen } from '@suite-native/firmware'; import { DeviceSettingsStackParamList, DeviceStackRoutes, @@ -11,6 +10,7 @@ import { DeviceAuthenticityStackNavigator } from './DeviceAuthenticityStackNavig import { DevicePinProtectionStackNavigator } from './DevicePinProtectionStackNavigator'; import { ContinueOnTrezorScreen } from '../screens/ContinueOnTrezorScreen'; import { DeviceSettingsModalScreen } from '../screens/DeviceSettingsModalScreen'; +import { FirmwareInstallationScreen } from '../screens/FirmwareInstallationScreen'; import { FirmwareUpdateScreen } from '../screens/FirmwareUpdateScreen/FirmwareUpdateScreen'; const DeviceSettingsStack = createNativeStackNavigator(); @@ -40,8 +40,8 @@ export const DeviceSettingsStackNavigator = () => ( component={ContinueOnTrezorScreen} /> ); diff --git a/suite-native/module-device-settings/src/screens/FirmwareInstallationScreen.tsx b/suite-native/module-device-settings/src/screens/FirmwareInstallationScreen.tsx new file mode 100644 index 00000000000..287c19ff926 --- /dev/null +++ b/suite-native/module-device-settings/src/screens/FirmwareInstallationScreen.tsx @@ -0,0 +1,41 @@ +import { useNavigation } from '@react-navigation/native'; + +import { FirmwareInstallationScreenContent } from '@suite-native/firmware'; +import { + DeviceSettingsStackParamList, + DeviceStackRoutes, + Screen, + StackNavigationProps, +} from '@suite-native/navigation'; + +type NavigationProp = StackNavigationProps< + DeviceSettingsStackParamList, + DeviceStackRoutes.FirmwareInstallation +>; + +export const FirmwareInstallationScreen = () => { + const navigation = useNavigation(); + + const handleFirmwareInstallationSuccess = () => { + const initialRoute = navigation.getState().routes.at(0)?.name; + if (initialRoute) { + navigation.navigate(initialRoute); + } else { + // This cause should not happen, but just to be safe + navigation.popToTop(); + } + }; + + const handleFirmwareInstallationFailure = () => { + navigation.navigate(DeviceStackRoutes.FirmwareUpdate); + }; + + return ( + + + + ); +}; diff --git a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx index 26eec005fc7..106c39663c1 100644 --- a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx +++ b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx @@ -55,7 +55,7 @@ export const FirmwareUpdateScreen = () => { 'moduleDeviceSettings.firmware.seedBottomSheet.continueButton', ), onPressPrimaryButton: () => { - navigation.navigate(DeviceStackRoutes.FirmwareUpdateInProgress); + navigation.navigate(DeviceStackRoutes.FirmwareInstallation); }, secondaryButtonTitle: translate( 'moduleDeviceSettings.firmware.seedBottomSheet.closeButton', diff --git a/suite-native/navigation/src/navigators.ts b/suite-native/navigation/src/navigators.ts index 9b3510681ab..dc60dfa4a1b 100644 --- a/suite-native/navigation/src/navigators.ts +++ b/suite-native/navigation/src/navigators.ts @@ -123,6 +123,7 @@ export type OnboardingStackParamList = { suspicionCause: DeviceSuspicionCause; }; [OnboardingStackRoutes.SecurityCheck]: undefined; + [OnboardingStackRoutes.FirmwareInstallationScreen]: undefined; }; export type AccountsImportStackParamList = { @@ -167,7 +168,7 @@ export type DeviceSettingsStackParamList = { [DeviceStackRoutes.DevicePinProtection]: undefined; [DeviceStackRoutes.DeviceAuthenticity]: undefined; [DeviceStackRoutes.FirmwareUpdate]: undefined; - [DeviceStackRoutes.FirmwareUpdateInProgress]: undefined; + [DeviceStackRoutes.FirmwareInstallation]: undefined; [DeviceStackRoutes.ContinueOnTrezor]: undefined; }; diff --git a/suite-native/navigation/src/routes.ts b/suite-native/navigation/src/routes.ts index a54bb677935..477444e2425 100644 --- a/suite-native/navigation/src/routes.ts +++ b/suite-native/navigation/src/routes.ts @@ -33,6 +33,7 @@ export enum OnboardingStackRoutes { UninitializedDeviceLanding = 'UninitializedDeviceLanding', SuspiciousDevice = 'SuspiciousDevice', SecurityCheck = 'SecurityCheck', + FirmwareInstallationScreen = 'FirmwareInstallationScreen', } export enum AccountsImportStackRoutes { @@ -47,7 +48,7 @@ export enum DeviceStackRoutes { DevicePinProtection = 'DevicePinProtection', DeviceAuthenticity = 'DeviceAuthenticity', FirmwareUpdate = 'FirmwareUpdate', - FirmwareUpdateInProgress = 'FirmwareUpdateInProgress', + FirmwareInstallation = 'FirmwareInstallation', ContinueOnTrezor = 'ContinueOnTrezor', } From e74a13ddb78d7675bd2bdbd1c8e805e9403d141d Mon Sep 17 00:00:00 2001 From: Petr Knetl Date: Wed, 19 Feb 2025 14:09:30 +0100 Subject: [PATCH 2/2] feat(suite-native): installation of FW in onboarding --- .../navigation/OnboardingStackNavigator.tsx | 5 ++++ .../screens/FirmwareInstallationScreen.tsx | 23 +++++++++++++++++++ .../src/screens/SecurityCheckScreen.tsx | 19 ++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx diff --git a/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx b/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx index c933f69beb0..54b61a7b108 100644 --- a/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx +++ b/suite-native/module-onboarding/src/navigation/OnboardingStackNavigator.tsx @@ -8,6 +8,7 @@ import { import { AnalyticsConsentScreen } from '../screens/AnalyticsConsentScreen'; import { BiometricsScreen } from '../screens/BiometricsScreen'; +import { FirmwareInstallationScreen } from '../screens/FirmwareInstallationScreen'; import { SecurityCheckScreen } from '../screens/SecurityCheckScreen'; import { SuspiciousDeviceScreen } from '../screens/SuspiciousDeviceScreen'; import { UninitializedDeviceLandingScreen } from '../screens/UninitializedDeviceLandingScreen'; @@ -41,5 +42,9 @@ export const OnboardingStackNavigator = () => ( name={OnboardingStackRoutes.SecurityCheck} component={SecurityCheckScreen} /> + ); diff --git a/suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx b/suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx new file mode 100644 index 00000000000..c33f614014d --- /dev/null +++ b/suite-native/module-onboarding/src/screens/FirmwareInstallationScreen.tsx @@ -0,0 +1,23 @@ +import { FirmwareInstallationScreenContent } from '@suite-native/firmware'; +import { Screen } from '@suite-native/navigation'; +import { useToast } from '@suite-native/toasts'; + +export const FirmwareInstallationScreen = () => { + const { showToast } = useToast(); + const handleFirmwareInstallationSuccess = () => { + showToast({ + variant: 'warning', + message: 'Firmware installation successful! TODO: redirect to the device AC screen.', + }); + }; + + return ( + + null} + /> + + ); +}; diff --git a/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx b/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx index 09356ecc718..9960eb2bf9a 100644 --- a/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx +++ b/suite-native/module-onboarding/src/screens/SecurityCheckScreen.tsx @@ -4,8 +4,14 @@ import { TitleHeader, VStack } from '@suite-native/atoms'; import { IconName } from '@suite-native/icons'; import { Translation } from '@suite-native/intl'; import { Link } from '@suite-native/link'; -import { DeviceSuspicionCause, Screen, ScreenHeader } from '@suite-native/navigation'; -import { useToast } from '@suite-native/toasts'; +import { + DeviceSuspicionCause, + OnboardingStackParamList, + OnboardingStackRoutes, + Screen, + ScreenHeader, + StackProps, +} from '@suite-native/navigation'; import { TREZOR_RESELLERS_URL } from '@trezor/urls'; import { SecurityCheckStepCard } from '../components/SecurityCheckStepCard'; @@ -56,8 +62,9 @@ const stepToContentMap = { } >; -export const SecurityCheckScreen = () => { - const { showToast } = useToast(); +export const SecurityCheckScreen = ({ + navigation, +}: StackProps) => { const [currentStep, setCurrentStep] = useState(1); const handlePressConfirmButton = () => { @@ -66,8 +73,8 @@ export const SecurityCheckScreen = () => { return; } - // TODO: navigate to Firmware install - showToast({ variant: 'warning', message: 'TODO: implement next screen' }); + + navigation.navigate(OnboardingStackRoutes.FirmwareInstallationScreen); }; return (