diff --git a/packages/suite/src/reducers/onboarding/onboardingReducer.ts b/packages/suite/src/reducers/onboarding/onboardingReducer.ts index a7298eeee3d..d4bd4970a4b 100644 --- a/packages/suite/src/reducers/onboarding/onboardingReducer.ts +++ b/packages/suite/src/reducers/onboarding/onboardingReducer.ts @@ -6,7 +6,7 @@ import { OnboardingAnalytics } from '@trezor/suite-analytics'; import { ONBOARDING } from 'src/actions/onboarding/constants'; import * as STEP from 'src/constants/onboarding/steps'; import type { AnyPath, AnyStepId } from 'src/types/onboarding'; -import { Action, TrezorDevice } from 'src/types/suite'; +import { Action } from 'src/types/suite'; export interface OnboardingRootState { onboarding: OnboardingState; @@ -26,7 +26,7 @@ export type BackupType = (typeof selectBackupTypes)[number]; export interface OnboardingState { backupType: BackupType; isActive: boolean; - prevDevice: TrezorDevice | null; + prevDeviceId: string | null; activeStepId: AnyStepId; path: AnyPath[]; onboardingAnalytics: Partial; @@ -40,7 +40,7 @@ const initialState: OnboardingState = { // prevDevice is used only in firmwareUpdate so maybe move it to firmwareUpdate // and here leave only isMatchingPrevDevice ? - prevDevice: null, + prevDeviceId: null, activeStepId: STEP.ID_FIRMWARE_STEP, path: [], onboardingAnalytics: {}, @@ -82,7 +82,7 @@ const onboarding = (state: OnboardingState = initialState, action: Action) => { draft.path = removePath(action.payload, state); break; case DEVICE.DISCONNECT: - draft.prevDevice = action.payload; + draft.prevDeviceId = action.payload.id ?? null; break; case ONBOARDING.ANALYTICS: draft.onboardingAnalytics = { ...state.onboardingAnalytics, ...action.payload }; diff --git a/packages/suite/src/types/suite/index.ts b/packages/suite/src/types/suite/index.ts index 7aa9295ff31..015447405ec 100644 --- a/packages/suite/src/types/suite/index.ts +++ b/packages/suite/src/types/suite/index.ts @@ -9,7 +9,8 @@ import { messageSystemActions } from '@suite-common/message-system'; import type { Route } from '@suite-common/suite-types'; import { notificationsActions } from '@suite-common/toast-notifications'; import { deviceActions, discoveryActions, transactionsActions } from '@suite-common/wallet-core'; -import type { BlockchainEvent, TransportEvent, UiEvent } from '@trezor/connect'; +import { BlockchainEvent, DEVICE, DeviceEvent, TransportEvent, UiEvent } from '@trezor/connect'; +import { FilterOutFromUnionByTypeProperty } from '@trezor/type-utils'; import type { BackupAction } from 'src/actions/backup/backupActions'; import type { OnboardingAction } from 'src/actions/onboarding/onboardingActions'; @@ -41,7 +42,15 @@ export type { TrezorDevice, } from '@suite-common/suite-types'; -type TrezorConnectEvents = TransportEvent | UiEvent | BlockchainEvent; +type FilteredDeviceEvents = FilterOutFromUnionByTypeProperty< + DeviceEvent, + 'type', + // Those types are remapped onto different actions in the connectInitThunks.ts and not used directly + // as the rest of the DeviceEvents. + typeof DEVICE.CONNECT | typeof DEVICE.CONNECT_UNACQUIRED +>; + +type TrezorConnectEvents = TransportEvent | UiEvent | FilteredDeviceEvents | BlockchainEvent; export type TransactionAction = ReturnType< (typeof transactionsActions)[keyof typeof transactionsActions] @@ -62,7 +71,7 @@ type DeviceAuthenticityAction = ReturnType< // all actions from all apps used to properly type Dispatch. export type Action = - | TrezorConnectEvents // Todo: This should not be here, actions shall be defined independently from Connect Events (and they shall be mapped onto them) + | TrezorConnectEvents | RouterAction | WindowAction | StorageAction @@ -113,6 +122,7 @@ export type ForegroundAppProps = { export type ToastNotificationVariant = 'success' | 'info' | 'warning' | 'error' | 'transparent'; export { TorStatus } from '@trezor/suite-desktop-api/src/enums'; + export interface TorBootstrap { current: number; total: number; diff --git a/packages/suite/src/views/onboarding/UnexpectedState/index.tsx b/packages/suite/src/views/onboarding/UnexpectedState/index.tsx index e3f5cc18a60..2a06f4254e6 100644 --- a/packages/suite/src/views/onboarding/UnexpectedState/index.tsx +++ b/packages/suite/src/views/onboarding/UnexpectedState/index.tsx @@ -27,12 +27,11 @@ const UnexpectedState = ({ children }: UnexpectedStateProps) => { const device = useSelector(selectSelectedDevice); const prerequisite = useSelector(selectPrerequisite); - const { prevDevice, activeStepId, showPinMatrix } = useOnboarding(); + const { prevDeviceId, activeStepId, showPinMatrix } = useOnboarding(); const activeStep = steps.find(s => s.id === activeStepId); const isNotSameDevice = useMemo(() => { - const prevDeviceId = prevDevice?.id; // if no device was connected before, assume it is same device if (!prevDeviceId) { return false; @@ -44,7 +43,7 @@ const UnexpectedState = ({ children }: UnexpectedStateProps) => { } return deviceId !== prevDeviceId; - }, [prevDevice, device]); + }, [prevDeviceId, device]); const UnexpectedStateComponent = useMemo(() => { if (!activeStep?.prerequisites) return null; diff --git a/packages/type-utils/src/utils.ts b/packages/type-utils/src/utils.ts index 6222560cef1..9f0b807e617 100644 --- a/packages/type-utils/src/utils.ts +++ b/packages/type-utils/src/utils.ts @@ -108,3 +108,27 @@ export type DefinedUnionMember = T extends string ? T : never; export type FilterPropertiesByType = { [Key in keyof T as T[Key] extends ValueFilter ? Key : never]: T[Key]; }; + +/** + * Removed the type from the union where `{ KeyName: ValueToExclude }`. + * + * Example: + * ``` + * type T1 = + * | { type: 'A'; a: string } + * | { type: 'B'; b: number } + * | { type: 'C' | 'D' | 'E'; cde: boolean }; + * + * // { type: 'A', a: string } | { type: 'B', b: number } | { type: 'D' | 'E', cde: boolean }; + * type NotC = FilterOutFromUnionByTypeProperty; + * ``` + */ +export type FilterOutFromUnionByTypeProperty< + Union, + KeyName extends keyof Union, + ValueToExclude extends Union[KeyName], +> = Union extends { [K in KeyName]: infer ActualValue } + ? ActualValue extends ValueToExclude + ? never + : { [K in KeyName]: Exclude } & Omit + : Union; diff --git a/suite-common/connect-init/src/connectInitThunks.ts b/suite-common/connect-init/src/connectInitThunks.ts index a96a96e1098..06ab9155c24 100644 --- a/suite-common/connect-init/src/connectInitThunks.ts +++ b/suite-common/connect-init/src/connectInitThunks.ts @@ -35,11 +35,12 @@ export const connectInitThunk = createThunk( // set event listeners and dispatch as TrezorConnect.on(DEVICE_EVENT, ({ event: _, ...eventData }) => { - // dispatch event as action - if (eventData.type === DEVICE.CONNECT || eventData.type === DEVICE.CONNECT_UNACQUIRED) { + // This special case here allows us to "inject" extra data into action's payload + // and change the type of the action (in this case DeviceEvent type !== Redux Action type) dispatch(deviceConnectThunks({ type: eventData.type, device: eventData.payload })); } else { + // dispatch event as action dispatch({ type: eventData.type, payload: eventData.payload }); } }); diff --git a/suite-common/wallet-core/src/device/deviceActions.ts b/suite-common/wallet-core/src/device/deviceActions.ts index 23e3cc8bfec..a931bf02f05 100644 --- a/suite-common/wallet-core/src/device/deviceActions.ts +++ b/suite-common/wallet-core/src/device/deviceActions.ts @@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit'; import { ButtonRequest, TrezorDevice } from '@suite-common/suite-types'; import { WalletType } from '@suite-common/wallet-types'; -import { DEVICE, Device, DeviceVersionChanged } from '@trezor/connect'; +import { DEVICE, Device } from '@trezor/connect'; export const DEVICE_MODULE_PREFIX = '@suite/device'; @@ -29,16 +29,6 @@ const deviceDisconnect = createAction(DEVICE.DISCONNECT, (payload: TrezorDevice) payload, })); -// this action is not used but is required because of the typings -// changed here: https://github.com/trezor/trezor-suite/commit/c02412bccf80da7c827f624b7a7c85cdedf278c5#diff-2e9d057f0bfe2cc92fe50d4ce28838622d9e79fcca010ab8847a0fa288da13fd -// in fact action is dispatched from connectInitThunk same as the rest of events -const deviceFirmwareVersionChanged = createAction( - DEVICE.FIRMWARE_VERSION_CHANGED, - (payload: DeviceVersionChanged['payload']) => ({ - payload, - }), -); - const updatePassphraseMode = createAction( `${DEVICE_MODULE_PREFIX}/updatePassphraseMode`, (payload: { device: TrezorDevice; hidden: boolean; alwaysOnDevice?: boolean }) => ({ payload }), @@ -118,5 +108,4 @@ export const deviceActions = { updateSelectedDevice, removeButtonRequests, setEntropyCheckFail, - deviceFirmwareVersionChanged, }; diff --git a/suite-common/wallet-core/src/device/deviceThunks.ts b/suite-common/wallet-core/src/device/deviceThunks.ts index 63edeb7cb9b..693f884256f 100644 --- a/suite-common/wallet-core/src/device/deviceThunks.ts +++ b/suite-common/wallet-core/src/device/deviceThunks.ts @@ -44,7 +44,6 @@ type SelectDeviceThunkParams = { * Called from: * - `@trezor/connect` events handler `handleDeviceConnect`, `handleDeviceDisconnect` * - from user action in `@suite-components/DeviceMenu` - * @param {(Device | TrezorDevice | undefined)} device */ export const selectDeviceThunk = createThunk( `${DEVICE_MODULE_PREFIX}/selectDevice`,