Skip to content

Commit

Permalink
Refactor: LowGasAlert errors on EVM (#7782)
Browse files Browse the repository at this point in the history
* refactor: LowGasAlert errors on EVM

* chore: lint

* chore: I crave consistent behaviour

* chore: redirect only if buy supported but still display the error
  • Loading branch information
CremaFR committed Sep 13, 2024
1 parent 52ae4d3 commit 06a6ce1
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 61 deletions.
5 changes: 5 additions & 0 deletions .changeset/late-seals-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": minor
---

refactor: LowGasAlert to be consistent with all other errors
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useCallback } from "react";
import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";
import { setTrackingSource } from "~/renderer/analytics/TrackPage";
import { isCurrencySupported } from "~/renderer/screens/exchange/config";
import Alert from "~/renderer/components/Alert";
import { Account } from "@ledgerhq/types-live/lib/account";
import { Flex } from "@ledgerhq/react-ui";
import TranslatedError from "~/renderer/components/TranslatedError";

type LowGasAlertBuyMoreProps = {
account: Account;
handleRequestClose: () => void;
gasPriceError: Error | null;
trackingSource: string;
};

/**
* LowGasAlertBuyMore
*
* This component renders an alert message when the user has insufficient gas
* to complete a transaction.
* It also provides a call to action to buy more crypto,
* handling redirection to the exchange flow.
*
* Usage:
* <LowGasAlertBuyMore
* account={mainAccount}
* handleRequestClose={closeAllModal}
* gasPriceError={gasPriceError}
* trackingSource={"swap | send or whatever"}s
* />
*
*/
const LowGasAlertBuyMore = ({
account,
handleRequestClose,
gasPriceError,
trackingSource,
}: LowGasAlertBuyMoreProps) => {
const history = useHistory();
const dispatch = useDispatch();

const onBuyClick = useCallback(() => {
dispatch(handleRequestClose());
setTrackingSource(trackingSource);
history.push({
pathname: "/exchange",
state: {
currency: account.currency.id,
account: account.id,
mode: "buy",
},
});
}, [account.currency.id, account.id, history, dispatch, handleRequestClose, trackingSource]);

if (!gasPriceError) return null;
return (
<Flex onClick={isCurrencySupported("BUY", account.currency) ? onBuyClick : undefined}>
<Alert type="warning">
<TranslatedError error={gasPriceError} />
</Alert>
</Flex>
);
};

export default LowGasAlertBuyMore;
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,11 @@ import GasPriceField from "./GasPriceField";
import MaxFeeField from "./MaxFeeField";
import PriorityFeeField from "./PriorityFeeField";
import SelectFeeStrategy from "./SelectFeeStrategy";
import TranslatedError from "~/renderer/components/TranslatedError";
import Alert from "~/renderer/components/Alert";
import { Flex } from "@ledgerhq/react-ui";
import { closeAllModal } from "~/renderer/actions/modals";
import { setTrackingSource } from "~/renderer/analytics/TrackPage";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router";

const Root: NonNullable<EvmFamily["sendAmountFields"]>["component"] = props => {
const { account, updateTransaction, transaction } = props;
const bridge: AccountBridge<EvmTransaction> = getAccountBridge(account);

const { errors } = props.status;
const { gasPrice: messageGas, amount: messageAmount } = errors;
const dispatch = useDispatch();
const history = useHistory();

const onBuyClick = useCallback(() => {
dispatch(closeAllModal());
setTrackingSource("send flow");
history.push({
pathname: "/exchange",
state: {
currency: account.currency.id,
account: account.id,
mode: "buy", // buy or sell
},
});
}, [account.currency.id, account.id, dispatch, history]);

const [gasOptions, error, loading] = useGasOptions({
currency: account.currency,
transaction,
Expand Down Expand Up @@ -111,13 +86,6 @@ const Root: NonNullable<EvmFamily["sendAmountFields"]>["component"] = props => {
<SelectFeeStrategy gasOptions={gasOptions} onClick={onFeeStrategyClick} {...props} />
</>
)}
{(messageGas || messageAmount) && (
<Flex onClick={onBuyClick}>
<Alert type="warning" data-testid="alert-insufficient-funds-warning">
<TranslatedError error={messageGas || messageAmount} />
</Alert>
</Flex>
)}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Button from "~/renderer/components/Button";
import CurrencyDownStatusAlert from "~/renderer/components/CurrencyDownStatusAlert";
import ErrorBanner from "~/renderer/components/ErrorBanner";
import SpendableBanner from "~/renderer/components/SpendableBanner";
import BuyButton from "~/renderer/components/BuyButton";
import Label from "~/renderer/components/Label";
import Input from "~/renderer/components/Input";
import { useSelector } from "react-redux";
Expand All @@ -20,6 +19,8 @@ import SendAmountFields from "../SendAmountFields";
import AmountField from "../fields/AmountField";
import { StepProps } from "../types";
import { getLLDCoinFamily } from "~/renderer/families";
import { closeAllModal } from "~/renderer/actions/modals";
import LowGasAlertBuyMore from "~/renderer/families/evm/SendAmountFields/LowGasAlertBuyMore";

const StepAmount = (props: StepProps) => {
const {
Expand Down Expand Up @@ -60,6 +61,8 @@ const StepAmount = (props: StepProps) => {
}, [specific.nft, transaction]);

if (!status) return null;
const { errors } = status;
const { gasPrice } = errors;

return (
<Box flow={4}>
Expand Down Expand Up @@ -114,6 +117,12 @@ const StepAmount = (props: StepProps) => {
bridgePending={bridgePending}
updateTransaction={updateTransaction}
/>
<LowGasAlertBuyMore
account={mainAccount}
handleRequestClose={closeAllModal}
gasPriceError={gasPrice}
trackingSource={"send flow"}
/>
</Fragment>
</Box>
);
Expand All @@ -132,16 +141,12 @@ export class StepAmountFooter extends PureComponent<StepProps> {
const isTerminated = mainAccount.currency.terminated;
const hasErrors = Object.keys(errors).length;
const canNext = !bridgePending && !hasErrors && !isTerminated;
const { maxPriorityFee: maxPriorityFeeError, maxFee: maxFeeError } = errors;

return (
<>
{!isNFTSend ? (
<AccountFooter parentAccount={parentAccount} account={account} status={status} />
) : null}
{maxPriorityFeeError || maxFeeError ? (
<BuyButton currency={mainAccount.currency} account={mainAccount} />
) : null}
<Button
id={"send-amount-continue-button"}
isLoading={bridgePending}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import TrackPage from "~/renderer/analytics/TrackPage";
import Box from "~/renderer/components/Box";
import Button from "~/renderer/components/Button";
import CurrencyDownStatusAlert from "~/renderer/components/CurrencyDownStatusAlert";
import BuyButton from "~/renderer/components/BuyButton";
import { NotEnoughGas } from "@ledgerhq/errors";
import Alert from "~/renderer/components/Alert";
import TranslatedError from "~/renderer/components/TranslatedError";
import AccountFooter from "../AccountFooter";
import SendAmountFields from "../SendAmountFields";
import { StepProps } from "../types";
import LowGasAlertBuyMore from "~/renderer/families/evm/SendAmountFields/LowGasAlertBuyMore";
import { closeAllModal } from "~/renderer/actions/modals";
const StepAmount = ({
account,
parentAccount,
Expand All @@ -25,15 +25,19 @@ const StepAmount = ({
}: StepProps) => {
if (!status) return null;
const mainAccount = account ? getMainAccount(account, parentAccount) : null;
const { gasPrice: gasPriceError } = status.errors;

return (
<Box flow={4}>
<TrackPage category="Sign Transaction Flow" name="Step Amount" />
{mainAccount ? <CurrencyDownStatusAlert currencies={[mainAccount.currency]} /> : null}
{error || warning ? (
<Alert type={error ? "error" : "warning"}>
<TranslatedError error={error || warning} />
</Alert>
) : null}
{error || warning
? !gasPriceError && (
<Alert type={error ? "error" : "warning"}>
<TranslatedError error={error || warning} />
</Alert>
)
: null}
{account && transaction && mainAccount && (
<Fragment key={account.id}>
<SendAmountFields
Expand All @@ -47,6 +51,14 @@ const StepAmount = ({
/>
</Fragment>
)}
{mainAccount && gasPriceError && (
<LowGasAlertBuyMore
account={mainAccount}
handleRequestClose={closeAllModal}
gasPriceError={gasPriceError}
trackingSource={"sign flow"}
/>
)}
</Box>
);
};
Expand All @@ -64,13 +76,9 @@ export class StepAmountFooter extends PureComponent<StepProps> {
const isTerminated = mainAccount.currency.terminated;
const hasErrors = Object.keys(errors).length;
const canNext = !bridgePending && !hasErrors && !isTerminated;
const { gasPrice } = errors;
return (
<>
<AccountFooter parentAccount={parentAccount} account={account} status={status} />
{gasPrice && gasPrice instanceof NotEnoughGas ? (
<BuyButton currency={mainAccount.currency} account={mainAccount} />
) : null}
<Button
id={"sign-transaction-amount-continue-button"}
isLoading={bridgePending}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import { useGetSwapTrackingProperties } from "../../utils/index";
import { Account, AccountLike, FeeStrategy } from "@ledgerhq/types-live";
import { t } from "i18next";
import { Transaction } from "@ledgerhq/live-common/generated/types";
import { Button, Divider } from "@ledgerhq/react-ui";
import { Button, Divider, Flex } from "@ledgerhq/react-ui";
import { getAccountBridge } from "@ledgerhq/live-common/bridge/impl";
import { getMainAccount } from "@ledgerhq/live-common/account/index";
import LowGasAlertBuyMore from "~/renderer/families/evm/SendAmountFields/LowGasAlertBuyMore";
import TranslatedError from "~/renderer/components/TranslatedError";
import Alert from "~/renderer/components/Alert";

type Props = {
setTransaction: SwapTransactionType["setTransaction"];
mainAccount: AccountLike;
account: AccountLike;
parentAccount: Account;
status: SwapTransactionType["status"];
disableSlowStrategy?: boolean;
Expand All @@ -23,7 +27,7 @@ type Props = {

export default function FeesDrawerLiveApp({
setTransaction,
mainAccount,
account,
parentAccount,
status,
provider,
Expand All @@ -36,15 +40,17 @@ export default function FeesDrawerLiveApp({
const [isOpen, setIsOpen] = useState(true);
const [transaction, setTransactionState] = useState(initialTransaction);
const [transactionStatus, setTransactionStatus] = useState(status);
const mainAccount = getMainAccount(account, parentAccount);

const bridge = getAccountBridge(mainAccount, parentAccount);
const { amount: amountError, gasPrice: gasPriceError } = transactionStatus.errors;

const handleSetTransaction = useCallback(
(transaction: Transaction) => {
const account = mainAccount.type === "TokenAccount" ? parentAccount : mainAccount;
bridge
.prepareTransaction(account, transaction)
.prepareTransaction(mainAccount, transaction)
.then(preparedTransaction =>
bridge.getTransactionStatus(account, preparedTransaction).then(status => {
bridge.getTransactionStatus(mainAccount, preparedTransaction).then(status => {
setTransactionStatus(status);
setTransactionState(preparedTransaction);
setTransaction(preparedTransaction);
Expand All @@ -54,18 +60,17 @@ export default function FeesDrawerLiveApp({
console.error("Error preparing transaction:", error);
});
},
[setTransaction, bridge, mainAccount, parentAccount],
[setTransaction, bridge, mainAccount],
);

const handleUpdateTransaction = useCallback(
(updater: (arg0: Transaction) => Transaction) => {
const account = mainAccount.type === "TokenAccount" ? parentAccount : mainAccount;
setTransactionState(prevTransaction => {
let updatedTransaction = updater(prevTransaction);
bridge
.prepareTransaction(account, updatedTransaction)
.prepareTransaction(mainAccount, updatedTransaction)
.then(preparedTransaction =>
bridge.getTransactionStatus(account, preparedTransaction).then(status => {
bridge.getTransactionStatus(mainAccount, preparedTransaction).then(status => {
setTransactionStatus(status);
setTransaction(preparedTransaction);
updatedTransaction = preparedTransaction;
Expand All @@ -79,7 +84,7 @@ export default function FeesDrawerLiveApp({
return updatedTransaction;
});
},
[setTransaction, bridge, mainAccount, parentAccount],
[setTransaction, bridge, mainAccount],
);

const mapStrategies = useCallback(
Expand Down Expand Up @@ -130,6 +135,19 @@ export default function FeesDrawerLiveApp({
}}
/>
)}
<LowGasAlertBuyMore
account={mainAccount}
handleRequestClose={() => handleRequestClose(false)}
gasPriceError={gasPriceError}
trackingSource={"swap flow"}
/>
{amountError && !gasPriceError && (
<Flex>
<Alert type="warning">
<TranslatedError error={amountError} />
</Alert>
</Flex>
)}
</Box>
<Divider />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ const SwapWebView = ({
FeesDrawerLiveApp,
{
setTransaction,
mainAccount: fromAccount,
account: fromAccount,
parentAccount: fromParentAccount,
status: status,
provider: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ const SwapWebView = ({ manifest, liveAppUnavailable }: SwapWebProps) => {
FeesDrawerLiveApp,
{
setTransaction,
mainAccount: fromAccount,
account: fromAccount,
parentAccount: fromParentAccount,
status: status,
provider: undefined,
Expand Down

0 comments on commit 06a6ce1

Please sign in to comment.