refactor(suite): firmware thp-pairing step and views

This commit is contained in:
Szymon Lesisz
2026-02-17 12:10:45 +01:00
committed by Szymon Lesisz
parent 3c2bcd89ac
commit 65ae4bb342
12 changed files with 70 additions and 59 deletions

View File

@@ -22,7 +22,6 @@ export const FirmwareProgressBar = () => {
> = {
installing: 'TR_INSTALLING',
restarting: 'TR_RESTARTING_TREZOR',
thp: 'TR_FIRMWARE_STATUS_INSTALLATION_COMPLETED',
completed: 'TR_FIRMWARE_STATUS_INSTALLATION_COMPLETED',
};

View File

@@ -1,4 +1,4 @@
import { ReactNode, useState } from 'react';
import { ReactNode, useEffect, useState } from 'react';
import { Translation } from '@suite/intl';
import { Card, Column, Modal, Text } from '@trezor/components';
@@ -9,12 +9,16 @@ import { useDispatch } from '../../../hooks/suite';
type ThpPairingStartStepProps = {
modalHeading: ReactNode;
isLoading?: boolean;
};
// reflection of components/onboarding/ThpPairing/ThpPairingStartStep
export const ThpPairingStartStep = ({ modalHeading }: ThpPairingStartStepProps) => {
const [isLoading, setIsLoading] = useState(false);
export const ThpPairingStartStep = (props: ThpPairingStartStepProps) => {
const [isLoading, setIsLoading] = useState(props.isLoading);
const dispatch = useDispatch();
useEffect(() => {
setIsLoading(props.isLoading);
}, [props.isLoading]);
const onClick = () => {
setIsLoading(true);
@@ -25,7 +29,7 @@ export const ThpPairingStartStep = ({ modalHeading }: ThpPairingStartStepProps)
<Modal.ModalBase
onCancel={undefined} // intentionally NOT cancellable here, cancellable on the device only
data-testid="@firmware-modal"
heading={modalHeading}
heading={props.modalHeading}
bottomContent={
<Modal.Button onClick={onClick} isLoading={isLoading}>
<Translation id="TR_CONTINUE" />

View File

@@ -1,7 +1,8 @@
import { ReactNode } from 'react';
import { ReactNode, useRef } from 'react';
import { ThpStep } from '@suite-common/thp';
import { exhaustive } from '@trezor/type-utils';
import { selectThpStep } from '@suite-common/thp';
import { useSelector } from 'src/hooks/suite';
import { ThpCodeEntryStep } from './ThpCodeEntryStep';
import { ThpCodeInvalidStep } from './ThpCodeInvalidStep';
@@ -9,14 +10,16 @@ import { ThpPairingConfirmStep } from './ThpPairingConfirmStep';
import { ThpPairingStartStep } from './ThpPairingStartStep';
// reflection of components/onboarding/ThpPairingStep/ThpPairingStep.tsx
export const ThpPairingStep = ({
thpStep,
heading,
}: {
thpStep: NonNullable<ThpStep>;
heading: ReactNode;
}) => {
switch (thpStep) {
export const ThpPairingStep = ({ heading }: { heading: ReactNode }) => {
const thpStep = useSelector(selectThpStep);
const prevStepRef = useRef(thpStep);
if (thpStep) {
prevStepRef.current = thpStep;
}
// render thpState if set or last known step. fallback to ThpPairingStartStep with loader
const step = thpStep ?? prevStepRef.current;
switch (step) {
case 'BeforeConnectionInfo':
return <ThpPairingStartStep modalHeading={heading} />;
case 'ConfirmOnlyConnection':
@@ -28,6 +31,6 @@ export const ThpPairingStep = ({
return <ThpCodeInvalidStep modalHeading={heading} />;
default:
exhaustive(thpStep);
return <ThpPairingStartStep modalHeading={heading} isLoading />;
}
};

View File

@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Translation } from '@suite/intl';
@@ -7,9 +7,12 @@ import { OnboardingCard } from 'src/components/onboarding/OnboardingCard/Onboard
import { useDispatch } from 'src/hooks/suite';
// reflection of components/firmware/ThpPairing/ThpPairingStartStep
export const ThpPairingStartStep = () => {
const [isLoading, setIsLoading] = useState(false);
export const ThpPairingStartStep = (props: { isLoading?: boolean }) => {
const [isLoading, setIsLoading] = useState(props.isLoading);
const dispatch = useDispatch();
useEffect(() => {
setIsLoading(props.isLoading);
}, [props.isLoading]);
const onClick = () => {
setIsLoading(true);

View File

@@ -1,6 +1,7 @@
import { useRef } from 'react';
import { selectSelectedDevice } from '@suite-common/device';
import { ThpStep } from '@suite-common/thp';
import { exhaustive } from '@trezor/type-utils';
import { selectThpStep } from '@suite-common/thp';
import { useSelector } from 'src/hooks/suite/useSelector';
import { DeviceDisconnectedStep } from 'src/views/onboarding/UnexpectedState/DeviceDisconnectedStep';
@@ -11,14 +12,21 @@ import { ThpPairingConfirmStep } from './ThpPairingConfirmStep';
import { ThpPairingStartStep } from './ThpPairingStartStep';
// reflection of components/firmware/ThpPairingStep/ThpPairingStep.tsx
export const ThpPairingStep = ({ thpStep }: { thpStep: NonNullable<ThpStep> }) => {
export const ThpPairingStep = () => {
const device = useSelector(selectSelectedDevice);
const thpStep = useSelector(selectThpStep);
const prevStepRef = useRef(thpStep);
if (thpStep) {
prevStepRef.current = thpStep;
}
if (!device?.connected) {
return <DeviceDisconnectedStep />;
}
switch (thpStep) {
// render thpState if set or last known step. fallback to ThpPairingStartStep with loader
const step = thpStep ?? prevStepRef.current;
switch (step) {
case 'BeforeConnectionInfo':
return <ThpPairingStartStep />;
case 'ConfirmOnlyConnection':
@@ -30,6 +38,6 @@ export const ThpPairingStep = ({ thpStep }: { thpStep: NonNullable<ThpStep> }) =
return <ThpCodeInvalidStep />;
default:
exhaustive(thpStep);
return <ThpPairingStartStep isLoading />;
}
};

View File

@@ -1,7 +1,6 @@
import { ReactNode, useState } from 'react';
import { selectSelectedDevice } from '@suite-common/device';
import { selectThpStep } from '@suite-common/thp';
import { acquireDevice } from '@suite-common/wallet-core';
import { Modal } from '@trezor/components';
import { exhaustive } from '@trezor/type-utils';
@@ -36,8 +35,6 @@ export const FirmwareModal = ({
useFirmwareDesktopUpdate();
const device = useSelector(selectSelectedDevice);
const thpStep = useSelector(selectThpStep);
const dispatch = useDispatch();
const [isChecked, setIsChecked] = useState(false);
const { isProgressCheckDisplayed, handleDismissProgressCheck } =
@@ -57,10 +54,6 @@ export const FirmwareModal = ({
};
const getContent = () => {
if (thpStep !== null) {
return <ThpPairingStep thpStep={thpStep} heading={heading} />;
}
switch (status) {
case 'error':
return <StepError error={error} onClose={handleClose} />;
@@ -107,6 +100,9 @@ export const FirmwareModal = ({
isCustomFirmwareUploaded={isCustomFirmwareUploaded}
/>
);
case 'thp-pairing':
return <ThpPairingStep heading={heading} />;
case 'done':
return (
<StepDone

View File

@@ -2,7 +2,6 @@ import { useCallback, useEffect } from 'react';
import { Translation } from '@suite/intl';
import { selectSelectedDevice } from '@suite-common/device';
import { selectThpStep } from '@suite-common/thp';
import { Card } from '@trezor/components';
import { getFirmwareVersion } from '@trezor/device-utils';
import { exhaustive } from '@trezor/type-utils';
@@ -24,7 +23,6 @@ export const FirmwareStep = () => {
const modal = useSelector(state => state.modal);
const { goToNextStep, updateAnalytics } = useOnboarding();
const { error, resetReducer, firmwareUpdate, targetType, status } = useFirmwareDesktopUpdate();
const thpStep = useSelector(selectThpStep);
const { isProgressCheckDisplayed, handleDismissProgressCheck } =
useFirmwareInstallationProgressCheck();
@@ -139,10 +137,6 @@ export const FirmwareStep = () => {
return <DeviceDisconnectedStep />;
}
if (thpStep !== null) {
return <ThpPairingStep thpStep={thpStep} />;
}
switch (status) {
// check-seed is omitted as it is only relevant in separate fw update flow and it is not used in onboarding since user don't have any seed at that time
case 'initial':
@@ -165,6 +159,9 @@ export const FirmwareStep = () => {
onSuccess={goToNextStepAndResetReducer}
/>
);
case 'thp-pairing': {
return <ThpPairingStep />;
}
// This step does not make sense in onboarding; when installing firmware
// for the first time, there is no seed to be backed up before the firmware update.