fix(mobile): show NoBackup warning for xpub and receive address

This commit is contained in:
Matěj Husák
2025-06-17 10:32:06 +02:00
committed by Jiri Zbytovsky
parent bfd1968179
commit e3408a1a2f
7 changed files with 118 additions and 16 deletions

View File

@@ -537,7 +537,7 @@ export const en = {
title: 'Your Trezor wallet is not backed up',
subtitle: 'If your Trezor is lost or damaged, your funds may be irreversibly lost.',
cta: 'Create wallet backup',
continue: 'Continue anyway'
continue: 'Continue anyway',
},
confirmOnDeviceSheetTitle: 'Confirm on Trezor',
},

View File

@@ -32,10 +32,12 @@
"@suite-native/graph": "workspace:*",
"@suite-native/icons": "workspace:*",
"@suite-native/intl": "workspace:*",
"@suite-native/module-device-onboarding": "workspace:*",
"@suite-native/navigation": "workspace:*",
"@suite-native/qr-code": "workspace:*",
"@suite-native/tokens": "workspace:*",
"@suite-native/transactions": "workspace:*",
"@trezor/connect": "workspace:*",
"@trezor/styles": "workspace:*",
"@trezor/utils": "workspace:*",
"date-fns": "^4.1.0",

View File

@@ -1,19 +1,75 @@
import { useState } from 'react';
import { useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import { AccountsRootState, selectAccountByKey } from '@suite-common/wallet-core';
import { isAddressBasedNetwork } from '@suite-common/wallet-utils';
import { Button } from '@suite-native/atoms';
import { TrezorDevice } from '@suite-common/suite-types';
import {
AccountsRootState,
selectAccountByKey,
selectIsDeviceBackupRequired,
selectSelectedDevice,
} from '@suite-common/wallet-core';
import { Account } from '@suite-common/wallet-types';
import { getDerivationType, isAddressBasedNetwork } from '@suite-common/wallet-utils';
import { Button, useBottomSheetModal } from '@suite-native/atoms';
import { Translation } from '@suite-native/intl';
import { WalletBackupNotSetWarningBottomSheet } from '@suite-native/module-device-onboarding';
import { XpubQRCodeBottomSheet } from '@suite-native/qr-code';
import TrezorConnect, { Success, Unsuccessful } from '@trezor/connect';
import { convertTaprootXpub } from '@trezor/utils';
export const showXpubOnDevice = async (device: TrezorDevice, account: Account) => {
if (!device || !account) return;
const params = {
device,
path: account.path,
useEmptyPassphrase: device.useEmptyPassphrase,
showOnTrezor: true,
derivationType: getDerivationType(account.accountType),
coin: account.symbol,
};
let response: Success<unknown> | Unsuccessful;
switch (account.networkType) {
case 'bitcoin':
response = await TrezorConnect.getPublicKey(params);
break;
case 'cardano':
response = await TrezorConnect.cardanoGetPublicKey(params);
break;
case 'solana':
response = await TrezorConnect.solanaGetPublicKey(params);
break;
default:
response = {
success: false,
payload: { error: 'Method for getPublicKey not defined', code: undefined },
};
}
return response;
};
export const AccountSettingsShowXpubButton = ({ accountKey }: { accountKey: string }) => {
const account = useSelector((state: AccountsRootState) =>
selectAccountByKey(state, accountKey),
);
const [isXpubVisible, setIsXpubVisible] = useState(false);
const { bottomSheetRef, openModal, closeModal } = useBottomSheetModal();
const isDeviceBackupRequired = useSelector(selectIsDeviceBackupRequired);
const device = useSelector(selectSelectedDevice);
const showXpub = useCallback(() => {
if (!device || !account) return;
showXpubOnDevice(device, account);
if (isDeviceBackupRequired) {
openModal();
} else {
setIsXpubVisible(true);
}
}, [isDeviceBackupRequired, device, account, openModal]);
if (!account) return null;
@@ -39,11 +95,17 @@ export const AccountSettingsShowXpubButton = ({ accountKey }: { accountKey: stri
return (
<>
<Button
size="large"
onPress={() => setIsXpubVisible(true)}
colorScheme="tertiaryElevation0"
>
{isDeviceBackupRequired && (
<WalletBackupNotSetWarningBottomSheet
onConfirm={() => {
setIsXpubVisible(true);
closeModal();
}}
onClose={handleClose}
ref={bottomSheetRef}
/>
)}
<Button size="large" onPress={showXpub} colorScheme="tertiaryElevation0">
{buttonTitle}
</Button>
<XpubQRCodeBottomSheet

View File

@@ -28,10 +28,12 @@
{ "path": "../graph" },
{ "path": "../icons" },
{ "path": "../intl" },
{ "path": "../module-device-onboarding" },
{ "path": "../navigation" },
{ "path": "../qr-code" },
{ "path": "../tokens" },
{ "path": "../transactions" },
{ "path": "../../packages/connect" },
{ "path": "../../packages/styles" },
{ "path": "../../packages/utils" }
]

View File

@@ -60,10 +60,10 @@ export const WalletBackupNotSetWarningBottomSheet = forwardRef<
</Text>
<VStack spacing="sp12">
<Button colorScheme='yellowElevation0' onPress={handleContinueAnyway}>
<Button colorScheme="yellowElevation0" onPress={handleContinueAnyway}>
<Translation id="moduleDevice.noBackupModal.continue" />
</Button>
<Button colorScheme='yellowBold' onPress={handleCreateBackup}>
<Button colorScheme="yellowBold" onPress={handleCreateBackup}>
<Translation id="moduleDevice.noBackupModal.cta" />
</Button>
</VStack>

View File

@@ -1,18 +1,30 @@
import { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { G } from '@mobily/ts-belt';
import { getDisplaySymbol } from '@suite-common/wallet-config';
import { AccountsRootState, selectAccountByKey } from '@suite-common/wallet-core';
import {
AccountsRootState,
selectAccountByKey,
selectIsDeviceBackupRequired,
} from '@suite-common/wallet-core';
import { AccountKey, TokenAddress } from '@suite-common/wallet-types';
import { AccountDetailsCard } from '@suite-native/accounts';
import { Box, ErrorMessage, InlineAlertBox, VStack } from '@suite-native/atoms';
import {
Box,
ErrorMessage,
InlineAlertBox,
VStack,
useBottomSheetModal,
} from '@suite-native/atoms';
import {
ConfirmOnTrezorImage,
selectHasFirmwareAuthenticityCheckHardFailed,
} from '@suite-native/device';
import { Translation } from '@suite-native/intl';
import { Link } from '@suite-native/link';
import { WalletBackupNotSetWarningBottomSheet } from '@suite-native/module-device-onboarding';
import { CloseActionType, Screen } from '@suite-native/navigation';
import { ReceiveBlockedDeviceCompromisedScreen } from './ReceiveBlockedDeviceCompromisedScreen';
@@ -35,11 +47,26 @@ export const ReceiveAddressScreen = ({
const account = useSelector((state: AccountsRootState) =>
selectAccountByKey(state, accountKey),
);
const isDeviceBackupRequired = useSelector(selectIsDeviceBackupRequired);
const hasReceiveButtonRequest = useSelector(hasReceiveAddressButtonRequest);
const { bottomSheetRef, openModal, closeModal } = useBottomSheetModal();
const { address, isReceiveApproved, isUnverifiedAddressRevealed, handleShowAddress } =
useAccountReceiveAddress(accountKey);
const handleShowReceiveAddress = useCallback(() => {
handleShowAddress();
if (isDeviceBackupRequired) {
openModal();
}
}, [handleShowAddress, openModal, isDeviceBackupRequired]);
const closeNoBackupBottomSheet = useCallback(() => {
closeModal();
}, [closeModal]);
const hasFirmwareAuthenticityCheckHardFailed = useSelector(
selectHasFirmwareAuthenticityCheckHardFailed,
);
@@ -110,10 +137,17 @@ export const ReceiveAddressScreen = ({
isTokenAddress={!!tokenContract}
isReceiveApproved={isReceiveApproved}
isUnverifiedAddressRevealed={isUnverifiedAddressRevealed}
onShowAddress={handleShowAddress}
onShowAddress={handleShowReceiveAddress}
/>
</VStack>
</Box>
{isDeviceBackupRequired && (
<WalletBackupNotSetWarningBottomSheet
ref={bottomSheetRef}
onConfirm={closeNoBackupBottomSheet}
onClose={closeModal}
/>
)}
</Screen>
);
};

View File

@@ -10668,10 +10668,12 @@ __metadata:
"@suite-native/graph": "workspace:*"
"@suite-native/icons": "workspace:*"
"@suite-native/intl": "workspace:*"
"@suite-native/module-device-onboarding": "workspace:*"
"@suite-native/navigation": "workspace:*"
"@suite-native/qr-code": "workspace:*"
"@suite-native/tokens": "workspace:*"
"@suite-native/transactions": "workspace:*"
"@trezor/connect": "workspace:*"
"@trezor/styles": "workspace:*"
"@trezor/utils": "workspace:*"
date-fns: "npm:^4.1.0"