Skip to content

Commit

Permalink
[ENG-3340] feat: Add UI warning about the Non default sighash Ledger …
Browse files Browse the repository at this point in the history
…alert (#97)

* [ENG-3340] feat: Add UI warning about the Non default sighash Ledger alert

* Fix tooltip hiding

* Update the info message copy

* Create a separate LedgerStepView component

* Remove the unused imports

* Remove the Steps enum duplicate
  • Loading branch information
dhriaznov committed Mar 25, 2024
1 parent 59ae427 commit 66fafe6
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 61 deletions.
88 changes: 46 additions & 42 deletions src/app/components/confirmBtcTransaction/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import ledgerConnectDefaultIcon from '@assets/img/ledger/ledger_connect_default.svg';
import ledgerConnectBtcIcon from '@assets/img/ledger/ledger_import_connect_btc.svg';
import { delay } from '@common/utils/ledger';
import BottomModal from '@components/bottomModal';
import ActionButton from '@components/button';
import LedgerConnectionView from '@components/ledger/connectLedgerView';
import useWalletSelector from '@hooks/useWalletSelector';
import TransportFactory from '@ledgerhq/hw-transport-webusb';
import { btcTransaction, FungibleToken, Transport } from '@secretkeylabs/xverse-core';
import { FungibleToken, Transport, btcTransaction } from '@secretkeylabs/xverse-core';
import Callout from '@ui-library/callout';
import { StickyHorizontalSplitButtonContainer, StyledP } from '@ui-library/common.styled';
import Spinner from '@ui-library/spinner';
Expand All @@ -15,6 +12,7 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import SendLayout from '../../layouts/sendLayout';
import LedgerStepView, { Steps } from './ledgerStepView';
import TransactionSummary from './transactionSummary';

const LoaderContainer = styled.div(() => ({
Expand Down Expand Up @@ -97,7 +95,7 @@ function ConfirmBtcTransaction({
hasSigHashNone = false,
}: Props) {
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [currentStep, setCurrentStep] = useState(Steps.ConnectLedger);
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
const [isConnectSuccess, setIsConnectSuccess] = useState(false);
const [isConnectFailed, setIsConnectFailed] = useState(false);
Expand Down Expand Up @@ -138,7 +136,15 @@ function ConfirmBtcTransaction({

setIsConnectSuccess(true);
await delay(1500);
setCurrentStepIndex(1);

if (currentStep !== Steps.ExternalInputs && currentStep !== Steps.ConfirmTransaction) {
setCurrentStep(Steps.ExternalInputs);
return;
}

if (currentStep !== Steps.ConfirmTransaction) {
setCurrentStep(Steps.ConfirmTransaction);
}

try {
onConfirm(transport);
Expand All @@ -148,10 +154,16 @@ function ConfirmBtcTransaction({
}
};

const goToConfirmationStep = () => {
setCurrentStep(Steps.ConfirmTransaction);

handleConnectAndConfirm();
};

const handleRetry = async () => {
setIsTxRejected(false);
setIsConnectSuccess(false);
setCurrentStepIndex(0);
setCurrentStep(Steps.ConnectLedger);
};

// TODO: this is a bit naive, but should be correct. We may want to look at the sig hash types of the inputs instead
Expand Down Expand Up @@ -208,42 +220,34 @@ function ConfirmBtcTransaction({
)}
</SendLayout>
<BottomModal header="" visible={isModalVisible} onClose={() => setIsModalVisible(false)}>
{currentStepIndex === 0 && (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONNECT.TITLE')}
text={signatureRequestTranslate('LEDGER.CONNECT.SUBTITLE', { name: 'Bitcoin' })}
titleFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_SUBTITLE')}
imageDefault={ledgerConnectBtcIcon}
isConnectSuccess={isConnectSuccess}
isConnectFailed={isConnectFailed}
/>
)}
{currentStepIndex === 1 && (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONFIRM.TITLE')}
text={signatureRequestTranslate('LEDGER.CONFIRM.SUBTITLE')}
titleFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_SUBTITLE')}
imageDefault={ledgerConnectDefaultIcon}
isConnectSuccess={false}
isConnectFailed={isTxRejected}
/>
)}
<LedgerStepView
currentStep={currentStep}
isConnectSuccess={isConnectSuccess}
isConnectFailed={isConnectFailed}
isTxRejected={isTxRejected}
t={t}
signatureRequestTranslate={signatureRequestTranslate}
/>
<SuccessActionsContainer>
<ActionButton
onPress={isTxRejected || isConnectFailed ? handleRetry : handleConnectAndConfirm}
text={signatureRequestTranslate(
isTxRejected || isConnectFailed ? 'LEDGER.RETRY_BUTTON' : 'LEDGER.CONNECT_BUTTON',
)}
disabled={isButtonDisabled}
processing={isButtonDisabled}
/>
<ActionButton
onPress={onCancel}
text={signatureRequestTranslate('LEDGER.CANCEL_BUTTON')}
transparent
/>
{currentStep === Steps.ExternalInputs && !isTxRejected && !isConnectFailed ? (
<ActionButton onPress={goToConfirmationStep} text={t('LEDGER.CONTINUE_BUTTON')} />
) : (
<>
<ActionButton
onPress={isTxRejected || isConnectFailed ? handleRetry : handleConnectAndConfirm}
text={signatureRequestTranslate(
isTxRejected || isConnectFailed ? 'LEDGER.RETRY_BUTTON' : 'LEDGER.CONNECT_BUTTON',
)}
disabled={isButtonDisabled}
processing={isButtonDisabled}
/>
<ActionButton
onPress={onCancel}
text={signatureRequestTranslate('LEDGER.CANCEL_BUTTON')}
transparent
/>
</>
)}
</SuccessActionsContainer>
</BottomModal>
</>
Expand Down
87 changes: 87 additions & 0 deletions src/app/components/confirmBtcTransaction/ledgerStepView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import InfoIcon from '@assets/img/info.svg';
import ledgerConnectDefaultIcon from '@assets/img/ledger/ledger_connect_default.svg';
import ledgerConnectBtcIcon from '@assets/img/ledger/ledger_import_connect_btc.svg';
import LedgerConnectionView, {
ConnectLedgerContainer,
ConnectLedgerText,
} from '@components/ledger/connectLedgerView';
import LedgerFailView from '@components/ledger/failLedgerView';
import {
ConnectLedgerTitle,
InfoImage,
} from '@screens/ledger/confirmLedgerTransaction/index.styled';
import { TFunction } from 'react-i18next';

export enum Steps {
ConnectLedger = 0,
ExternalInputs = 1,
ConfirmTransaction = 2,
}

type Props = {
currentStep: Steps;
isConnectSuccess: boolean;
isConnectFailed: boolean;
isTxRejected: boolean;
t: TFunction<'translation', 'CONFIRM_TRANSACTION'>;
signatureRequestTranslate: TFunction<'translation', 'SIGNATURE_REQUEST'>;
};

function LedgerStepView({
currentStep,
isConnectSuccess,
isConnectFailed,
isTxRejected,
t,
signatureRequestTranslate,
}: Props) {
switch (currentStep) {
case Steps.ConnectLedger:
return (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONNECT.TITLE')}
text={signatureRequestTranslate('LEDGER.CONNECT.SUBTITLE', { name: 'Bitcoin' })}
titleFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_SUBTITLE')}
imageDefault={ledgerConnectBtcIcon}
isConnectSuccess={isConnectSuccess}
isConnectFailed={isConnectFailed}
/>
);
case Steps.ExternalInputs:
if (isTxRejected || isConnectFailed) {
return (
<LedgerFailView title={t('CONFIRM.ERROR_TITLE')} text={t('CONFIRM.ERROR_SUBTITLE')} />
);
}

return (
<div>
<ConnectLedgerContainer>
<InfoImage src={InfoIcon} alt="external inputs warning" />
<ConnectLedgerTitle textAlign="center">
{t('LEDGER.INPUTS_WARNING.EXTERNAL_INPUTS')} / <br />
{t('LEDGER.INPUTS_WARNING.NON_DEFAULT_SIGHASH')}
</ConnectLedgerTitle>
<ConnectLedgerText>{t('LEDGER.INPUTS_WARNING.SUBTITLE')}</ConnectLedgerText>
</ConnectLedgerContainer>
</div>
);
case Steps.ConfirmTransaction:
return (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONFIRM.TITLE')}
text={signatureRequestTranslate('LEDGER.CONFIRM.SUBTITLE')}
titleFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_SUBTITLE')}
imageDefault={ledgerConnectDefaultIcon}
isConnectSuccess={false}
isConnectFailed={isTxRejected}
/>
);
default:
return null;
}
}

export default LedgerStepView;
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ const OutputTitleText = styled(StyledP)((props) => ({
marginBottom: props.theme.space.s,
}));

const ExpandedContainer = styled(animated.div)({
const ExpandedContainer = styled(animated.div)((props) => ({
display: 'flex',
flexDirection: 'column',
marginTop: 16,
});
marginTop: props.theme.space.m,
}));

type Props = {
inputs: btcTransaction.EnhancedInput[];
Expand Down Expand Up @@ -76,7 +76,7 @@ function TxInOutput({ inputs, outputs }: Props) {
{isExpanded ? t('INPUT') : t('INPUT_AND_OUTPUT')}
</StyledP>
</Row>
<animated.img style={arrowRotation} src={DropDownIcon} alt="Drop Down" />
<animated.img style={arrowRotation} src={DropDownIcon} alt="Dropdown" />
</Button>
{isExpanded && (
<ExpandedContainer style={slideInStyles}>
Expand Down
3 changes: 2 additions & 1 deletion src/app/components/copyButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function CopyButton({ text }: Props) {
if (isCopied) {
setTimeout(() => {
setIsCopied(false);
}, 5000);
}, 2000);
}
}, [isCopied]);

Expand All @@ -62,6 +62,7 @@ function CopyButton({ text }: Props) {
content={t('COPIED')}
events={['click']}
place="top"
hidden={!isCopied}
/>
</>
);
Expand Down
1 change: 1 addition & 0 deletions src/app/components/ledger/ledgerAddressComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function LedgerAddressComponent({ title, address }: Props) {
content={isCopied ? 'Copied' : title}
events={['hover']}
place="bottom"
hidden={!isCopied}
/>
</AddressComponentContainer>
</AddressComponent>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/receiveCardComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function ReceiveCardComponent({
if (isCopied) {
setTimeout(() => {
setIsCopied(false);
}, 5000);
}, 2000);
}
}, [isCopied]);

Expand Down
6 changes: 1 addition & 5 deletions src/app/screens/confirmOrdinalTransaction/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ function ConfirmOrdinalTransaction() {
mutate({ signedTx: txHex });
};

const handleOnCancelClick = () => {
navigate(-1);
};

useResetUserFlow('/confirm-ordinal-tx');
const handleBackButtonClick = () => {
navigate(-1);
Expand All @@ -156,7 +152,7 @@ function ConfirmOrdinalTransaction() {
loadingBroadcastedTx={isLoading}
signedTxHex={signedTxHex}
onConfirmClick={handleOnConfirmClick}
onCancelClick={handleOnCancelClick}
onCancelClick={handleBackButtonClick}
ordinalTxUtxo={ordinalUtxo}
assetDetail={selectedOrdinal ? selectedOrdinal.number.toString() : ''}
currentFee={currentFee}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ export const InfoImage = styled.img({
height: 64,
});

export const ConnectLedgerTitle = styled.h1((props) => ({
export const ConnectLedgerTitle = styled.h1<{ textAlign?: 'left' | 'center' }>((props) => ({
...props.theme.headline_s,
textAlign: props.textAlign || 'left',
marginBottom: props.theme.spacing(6),
}));

Expand Down
6 changes: 3 additions & 3 deletions src/app/screens/ledger/confirmLedgerTransaction/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ import useWalletSelector from '@hooks/useWalletSelector';
import Transport from '@ledgerhq/hw-transport-webusb';
import { useTransition } from '@react-spring/web';
import {
Recipient,
StacksRecipient,
UTXO,
broadcastSignedTransaction,
microstacksToStx,
Recipient,
satsToBtc,
signLedgerMixedBtcTransaction,
signLedgerNativeSegwitBtcTransaction,
signLedgerStxTransaction,
StacksRecipient,
UTXO,
} from '@secretkeylabs/xverse-core';
import { DEFAULT_TRANSITION_OPTIONS } from '@utils/constants';
import { getBtcTxStatusUrl, getStxTxStatusUrl, getTruncatedAddress } from '@utils/helper';
Expand Down
5 changes: 1 addition & 4 deletions src/app/screens/restoreFunds/restoreOrdinals/ordinalRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,7 @@ function OrdinalRow({ ordinal, isLoading, disableTransfer, handleOrdinalTransfer
<ValueText>Ordinal</ValueText>
</ColumnContainer>
<ButtonContainer>
<TransferButton
onClick={() => handleOrdinalTransfer(ordinal)}
disabled={disableTransfer}
>
<TransferButton onClick={() => handleOrdinalTransfer(ordinal)} disabled={disableTransfer}>
{isLoading ? (
<LoaderContainer>
<Spinner color="white" size={15} />
Expand Down
6 changes: 6 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,13 @@
"TITLE": "Transaction broadcasted",
"SUBTITLE": "Your transaction have been successfully broadcasted. You can close this tab."
},
"INPUTS_WARNING": {
"EXTERNAL_INPUTS": "External inputs",
"NON_DEFAULT_SIGHASH": "Non-default sighash",
"SUBTITLE": "Your device may display a warning regarding external inputs and the use of default sighash signature type. This is expected as you'll be signing inputs from two addresses in one transaction, using a non-default signature type."
},
"CONFIRM_BUTTON": "Confirm",
"CONTINUE_BUTTON": "Continue",
"CONNECT_BUTTON": "Connect",
"CLOSE_BUTTON": "Close",
"CANCEL_BUTTON": "Cancel",
Expand Down

0 comments on commit 66fafe6

Please sign in to comment.