Skip to content

Commit

Permalink
Merge pull request #7696 from LedgerHQ/feat/sell-confirmation-message…
Browse files Browse the repository at this point in the history
…-lld

feat: confirmation message lld
  • Loading branch information
chrisduma-ledger authored Sep 4, 2024
2 parents ef99222 + 352846d commit 5a898af
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-otters-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": minor
---

Adds confirmation message for Sell in LLD
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export function isStartExchangeData(data: unknown): data is StartExchangeData {

export const LiveAppDrawer = () => {
const [dismissDisclaimerChecked, setDismissDisclaimerChecked] = useState<boolean>(false);
const { t } = useTranslation();
const dispatch = useDispatch();

// @ts-expect-error how to type payload?
const {
isOpen,
Expand All @@ -78,8 +81,6 @@ export const LiveAppDrawer = () => {
};
} = useSelector(platformAppDrawerStateSelector);

const { t } = useTranslation();
const dispatch = useDispatch();
const onContinue = useCallback(() => {
if (payload && payload.type === "DAPP_DISCLAIMER") {
const { manifest, disclaimerId, next } = payload;
Expand All @@ -90,11 +91,14 @@ export const LiveAppDrawer = () => {
next(manifest, dismissDisclaimerChecked);
}
}, [dismissDisclaimerChecked, dispatch, payload]);

const drawerContent = useMemo(() => {
if (!payload) {
return null;
}

const { type, manifest, data } = payload;

const action = createAction(connectApp, startExchange);
switch (type) {
case "DAPP_INFO":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { ExchangeSwap } from "@ledgerhq/live-common/exchange/swap/types";
import { getUpdateAccountWithUpdaterParams } from "@ledgerhq/live-common/exchange/swap/getUpdateAccountWithUpdaterParams";
import { useBroadcast } from "@ledgerhq/live-common/hooks/useBroadcast";
import { Transaction } from "@ledgerhq/live-common/generated/types";
import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { CryptoCurrency, Currency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { updateAccountWithUpdater } from "~/renderer/actions/accounts";
import { BodyContent, BodyContentProps } from "./BodyContent";
import { BodyContent } from "./BodyContent";
import { BigNumber } from "bignumber.js";
import { AccountLike } from "@ledgerhq/types-live";
import { DisabledTransactionBroadcastError } from "@ledgerhq/errors";
import { useRedirectToSwapHistory } from "~/renderer/screens/exchange/Swap2/utils";
import { getEnv } from "@ledgerhq/live-env";
import styled from "styled-components";
import { ExchangeType } from "@ledgerhq/live-common/wallet-api/react";

export type Data = {
provider: string;
Expand All @@ -31,6 +32,21 @@ export type Data = {
magnitudeAwareRate?: BigNumber;
};

export enum ExchangeModeEnum {
Sell = "sell",
Swap = "swap",
}

export type ExchangeMode = "sell" | "swap";

type ResultsState = {
mode: ExchangeMode;
swapId?: string;
provider: string;
sourceCurrency: Currency;
targetCurrency?: Currency;
};

export function isCompleteExchangeData(data: unknown): data is Data {
if (data === null || typeof data !== "object") {
return false;
Expand All @@ -52,9 +68,7 @@ const Body = ({ data, onClose }: { data: Data; onClose?: () => void | undefined
const dispatch = useDispatch();
const { onResult, onCancel, swapId, magnitudeAwareRate, ...exchangeParams } = data;
const { exchange, provider, transaction: transactionParams } = exchangeParams;

const { fromAccount: account, fromParentAccount: parentAccount } = exchange;

// toAccount exists only in swap mode
const toAccount = "toAccount" in exchange ? exchange.toAccount : undefined;

Expand Down Expand Up @@ -102,7 +116,7 @@ const Body = ({ data, onClose }: { data: Data; onClose?: () => void | undefined
const [transaction, setTransaction] = useState<Transaction>();
const [signedOperation, setSignedOperation] = useState<SignedOperation>();
const [error, setError] = useState<Error>();
const [result, setResult] = useState<BodyContentProps["result"]>();
const [result, setResult] = useState<ResultsState>();

const signRequest = useMemo(
() =>
Expand Down Expand Up @@ -140,47 +154,87 @@ const Body = ({ data, onClose }: { data: Data; onClose?: () => void | undefined
[dispatch, exchange, transactionParams, provider],
);

const onBroadcastSuccess = useCallback(
(operation: Operation) => {
// If swap we save to swap history and keep open the drawer
if (swapId && toAccount && magnitudeAwareRate && sourceCurrency && targetCurrency) {
const newResult = {
operation,
swapId,
};
updateAccount({
result: newResult,
magnitudeAwareRate,
});
setResult({
const getResultByTransactionType = (
isSwapTransaction: "" | CryptoCurrency | TokenCurrency | null | undefined,
) => {
return isSwapTransaction
? {
swapId,
mode: ExchangeModeEnum.Swap,
provider,
sourceCurrency,
targetCurrency,
});

if (getEnv("DISABLE_TRANSACTION_BROADCAST")) {
return onCancel(new DisabledTransactionBroadcastError());
sourceCurrency: sourceCurrency as Currency,
targetCurrency: targetCurrency as Currency,
}
onResult(operation);
// else not swap i.e card and sell we close the drawer
: {
provider,
mode: ExchangeModeEnum.Sell,
sourceCurrency: sourceCurrency as Currency,
};
};

const handleTransactionResult = (result: ResultsState, operation: Operation) => {
setResult(result);

onResult(operation);
};

const handleSwapTransaction = (operation: Operation, result: ResultsState) => {
const newResult = {
operation,
swapId: swapId as string,
};

updateAccount({
result: newResult,
magnitudeAwareRate: magnitudeAwareRate as BigNumber,
});

handleTransactionResult(result, operation);
};

const handleSellTransaction = (operation: Operation, result: ResultsState) => {
handleTransactionResult(result, operation);
};

const onBroadcastSuccess = useCallback(
(operation: Operation) => {
if (getEnv("DISABLE_TRANSACTION_BROADCAST")) {
return onCancel(new DisabledTransactionBroadcastError());
}

const isSwapTransaction =
swapId && toAccount && magnitudeAwareRate && sourceCurrency && targetCurrency;

const isSellTransaction =
(data.exchangeType === ExchangeType.SELL || data.exchangeType === ExchangeType.SELL_NG) &&
sourceCurrency;

const result = getResultByTransactionType(isSwapTransaction);

if (isSwapTransaction) {
handleSwapTransaction(operation, result);
} else if (isSellTransaction) {
handleSellTransaction(operation, result);
} else {
// old platform exchange flow
onResult(operation);
onClose?.();
}
},
// Disabling exhaustive-deps because adding handleSellTransaction and handleSwapTransaction would make it change on every render
// eslint-disable-next-line react-hooks/exhaustive-deps
[
setResult,
onResult,
onCancel,
onClose,
updateAccount,
swapId,
toAccount,
magnitudeAwareRate,
provider,
sourceCurrency,
targetCurrency,
swapId,
toAccount,
data.exchangeType,
updateAccount,
provider,
onResult,
onCancel,
onClose,
],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import connectApp from "@ledgerhq/live-common/hw/connectApp";
import { createAction } from "@ledgerhq/live-common/hw/actions/completeExchange";
import { createAction as txCreateAction } from "@ledgerhq/live-common/hw/actions/transaction";
import completeExchange from "@ledgerhq/live-common/exchange/platform/completeExchange";
import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { Currency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import DeviceAction from "~/renderer/components/DeviceAction";
import BigSpinner from "~/renderer/components/BigSpinner";
import ErrorDisplay from "~/renderer/components/ErrorDisplay";
import { TransactionBroadcastedContent } from "./TransactionBroadcastedContent";
import { SwapSelectorStateType } from "@ledgerhq/live-common/exchange/swap/types";
import { ExchangeMode } from "./Body";

const exchangeAction = createAction(completeExchange);
const sendAction = txCreateAction(connectApp);
Expand All @@ -37,10 +37,11 @@ export type BodyContentProps = {
amountExpectedTo?: number;
};
result?: {
swapId: string;
swapId?: string;
mode: ExchangeMode;
provider: string;
sourceCurrency: SwapSelectorStateType["currency"];
targetCurrency: SwapSelectorStateType["currency"];
sourceCurrency: Currency;
targetCurrency?: Currency;
};
onOperationSigned: (value: SignedOperation) => void;
onTransactionComplete: (value: Transaction) => void;
Expand All @@ -58,6 +59,7 @@ export const BodyContent = (props: BodyContentProps) => {
return (
<TransactionBroadcastedContent
swapId={props.result.swapId}
mode={props.result.mode}
provider={props.result.provider}
sourceCurrency={props.result.sourceCurrency}
targetCurrency={props.result.targetCurrency}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React from "react";
import { Trans } from "react-i18next";
import { SwapSelectorStateType } from "@ledgerhq/live-common/exchange/swap/types";
import Box from "~/renderer/components/Box";
import { Separator } from "~/renderer/screens/exchange/Swap2/Form/Separator";
import Button from "~/renderer/components/Button";
import SwapCompleted from "~/renderer/screens/exchange/Swap2/Form/ExchangeDrawer/SwapCompleted";
import { useGetSwapTrackingProperties } from "~/renderer/screens/exchange/Swap2/utils";
import TrackPage from "~/renderer/analytics/TrackPage";
import { Currency } from "@ledgerhq/types-cryptoassets";
import SellCompleted from "~/renderer/screens/exchange/Sell/SellCompleted";
import { ExchangeMode, ExchangeModeEnum } from "./Body";

type TransactionBroadcastedContentProps = {
swapId: string;
swapId?: string;
mode: ExchangeMode;
provider: string;
sourceCurrency: SwapSelectorStateType["currency"];
targetCurrency: SwapSelectorStateType["currency"];
sourceCurrency: Currency;
targetCurrency?: Currency;
onViewDetails: (id: string) => void;
};

export function TransactionBroadcastedContent(props: TransactionBroadcastedContentProps) {
const { swapId, provider, sourceCurrency, targetCurrency, onViewDetails } = props;

const { swapId, provider, sourceCurrency, targetCurrency, onViewDetails, mode } = props;
const swapDefaultTrack = useGetSwapTrackingProperties();

return (
<Box height="100%" justifyContent="space-between" paddingTop={62} paddingBottom={15}>
<TrackPage
Expand All @@ -28,26 +31,34 @@ export function TransactionBroadcastedContent(props: TransactionBroadcastedConte
sourceCurrency={sourceCurrency?.name}
targetCurrency={targetCurrency?.name}
provider={provider}
{...swapDefaultTrack}
{...(mode === ExchangeModeEnum.Swap && swapId && swapDefaultTrack)}
/>
{targetCurrency && (
<Box justifyContent="center" flex={1}>
<SwapCompleted swapId={swapId} provider={provider} targetCurrency={targetCurrency.name} />
</Box>
{mode === ExchangeModeEnum.Swap && swapId && targetCurrency && (
<>
<Box justifyContent="center" flex={1}>
<SwapCompleted
swapId={swapId}
provider={provider}
targetCurrency={targetCurrency.name}
/>
</Box>
<Box flex={0}>
<Separator noMargin />
<Box flexDirection="row-reverse" pt={16}>
<Button primary onClick={() => swapId && onViewDetails(swapId)}>
<Trans i18nKey="swap2.exchangeDrawer.completed.seeDetails" />
</Button>
</Box>
</Box>
</>
)}
{mode === ExchangeModeEnum.Sell && sourceCurrency && (
<>
<Box>
<SellCompleted />
</Box>
</>
)}
<Box flex={0}>
<Separator noMargin />
<Box
style={{
flexDirection: "row-reverse",
}}
pt={16}
>
<Button primary onClick={() => onViewDetails(swapId)}>
<Trans i18nKey="swap2.exchangeDrawer.completed.seeDetails" />
</Button>
</Box>
</Box>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { Trans } from "react-i18next";
import Box from "~/renderer/components/Box";
import Text from "~/renderer/components/Text";
import IconCheck from "~/renderer/icons/Check";
import IconClock from "~/renderer/icons/Clock";
import { IconWrapper, WrapperClock } from "../shared/shared-styles";

const SellCompleted = () => {
return (
<Box alignItems="center">
<IconWrapper>
<IconCheck size={20} />
<WrapperClock>
<IconClock size={16} />
</WrapperClock>
</IconWrapper>
<Text mt={4} color="palette.text.shade100" ff="Inter|SemiBold" fontSize={5}>
<Trans i18nKey={`sell.exchangeDrawer.completed.title`} />
</Text>
<Text mt={13} textAlign="center" color="palette.text.shade50" ff="Inter|Regular" fontSize={4}>
<Trans i18nKey={`sell.exchangeDrawer.completed.description`} />
</Text>
</Box>
);
};

export default SellCompleted;
Loading

0 comments on commit 5a898af

Please sign in to comment.