diff --git a/.changeset/modern-otters-own.md b/.changeset/modern-otters-own.md new file mode 100644 index 000000000000..9097703318d2 --- /dev/null +++ b/.changeset/modern-otters-own.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": minor +--- + +Adds confirmation message for Sell in LLD diff --git a/apps/ledger-live-desktop/src/renderer/components/LiveAppDrawer.tsx b/apps/ledger-live-desktop/src/renderer/components/LiveAppDrawer.tsx index 49940f252f77..bb20bc94266d 100644 --- a/apps/ledger-live-desktop/src/renderer/components/LiveAppDrawer.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/LiveAppDrawer.tsx @@ -62,6 +62,9 @@ export function isStartExchangeData(data: unknown): data is StartExchangeData { export const LiveAppDrawer = () => { const [dismissDisclaimerChecked, setDismissDisclaimerChecked] = useState(false); + const { t } = useTranslation(); + const dispatch = useDispatch(); + // @ts-expect-error how to type payload? const { isOpen, @@ -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; @@ -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": diff --git a/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/Body.tsx b/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/Body.tsx index a9139b76adbe..1bd8e0495e0f 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/Body.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/Body.tsx @@ -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; @@ -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; @@ -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; @@ -102,7 +116,7 @@ const Body = ({ data, onClose }: { data: Data; onClose?: () => void | undefined const [transaction, setTransaction] = useState(); const [signedOperation, setSignedOperation] = useState(); const [error, setError] = useState(); - const [result, setResult] = useState(); + const [result, setResult] = useState(); const signRequest = useMemo( () => @@ -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, ], ); diff --git a/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/BodyContent.tsx b/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/BodyContent.tsx index 1816f6878494..6b62f9069d07 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/BodyContent.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Platform/Exchange/CompleteExchange/BodyContent.tsx @@ -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); @@ -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; @@ -58,6 +59,7 @@ export const BodyContent = (props: BodyContentProps) => { return ( 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 ( - {targetCurrency && ( - - - + {mode === ExchangeModeEnum.Swap && swapId && targetCurrency && ( + <> + + + + + + + + + + + )} + {mode === ExchangeModeEnum.Sell && sourceCurrency && ( + <> + + + + )} - - - - - - ); } diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Sell/SellCompleted.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Sell/SellCompleted.tsx new file mode 100644 index 000000000000..752d674ee603 --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Sell/SellCompleted.tsx @@ -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 ( + + + + + + + + + + + + + + + ); +}; + +export default SellCompleted; diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/ExchangeDrawer/SwapCompleted.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/ExchangeDrawer/SwapCompleted.tsx index f884f3ec07a1..9ca129c4de46 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/ExchangeDrawer/SwapCompleted.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/ExchangeDrawer/SwapCompleted.tsx @@ -12,23 +12,13 @@ import { GradientHover } from "~/renderer/drawers/OperationDetails/styledCompone import IconCheck from "~/renderer/icons/Check"; import IconClock from "~/renderer/icons/Clock"; import { openURL } from "~/renderer/linking"; -import { colors } from "~/renderer/styles/theme"; import { track } from "~/renderer/analytics/segment"; import { getSwapProvider, AdditionalProviderConfig, } from "@ledgerhq/live-common/exchange/providers/swap"; +import { IconWrapper, WrapperClock } from "../../../shared/shared-styles"; -const IconWrapper = styled(Box)` - background: ${colors.lightGreen}; - color: ${colors.positiveGreen}; - width: 50px; - height: 50px; - border-radius: 25px; - align-items: center; - justify-content: center; - position: relative; -`; const Pill = styled(Text)` user-select: text; border-radius: 4px; @@ -59,16 +49,6 @@ const SwapIdWrapper = styled(Box).attrs(p => ({ } } `; -const WrapperClock = styled(Box).attrs(() => ({ - bg: "palette.background.paper", - color: "palette.text.shade60", -}))` - border-radius: 50%; - position: absolute; - bottom: -2px; - right: -2px; - padding: 2px; -`; const SwapCompleted = ({ swapId, diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/shared/shared-styles.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/shared/shared-styles.tsx new file mode 100644 index 000000000000..d62b386efc98 --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/shared/shared-styles.tsx @@ -0,0 +1,26 @@ +import styled from "styled-components"; +import Box from "~/renderer/components/Box"; + +import { colors } from "~/renderer/styles/theme"; + +export const IconWrapper = styled(Box)` + background: ${colors.lightGreen}; + color: ${colors.positiveGreen}; + width: 50px; + height: 50px; + border-radius: 25px; + align-items: center; + justify-content: center; + position: relative; +`; + +export const WrapperClock = styled(Box).attrs(() => ({ + bg: "palette.background.paper", + color: "palette.text.shade60", +}))` + border-radius: 50%; + position: absolute; + bottom: -2px; + right: -2px; + padding: 2px; +`; diff --git a/apps/ledger-live-desktop/static/i18n/en/app.json b/apps/ledger-live-desktop/static/i18n/en/app.json index 1dceee90663b..88c6642f74d0 100644 --- a/apps/ledger-live-desktop/static/i18n/en/app.json +++ b/apps/ledger-live-desktop/static/i18n/en/app.json @@ -1869,8 +1869,8 @@ "targetAccount": "Target Account" }, "sell": { - "notice": "Verify the Sell details on your device before sending it. The addresses are exchanged securely so you don’t have to verify them.", - "confirm": "Confirm Sell transaction" + "notice": "Verify the details on your device before sending it. The addresses are exchanged securely so you don’t have to verify them.", + "confirm": "Confirm transaction" }, "fund": { "notice": "Verify the Fund details on your device before sending it. The addresses are exchanged securely so you don't have to verify them.", @@ -2190,7 +2190,13 @@ }, "sell": { "title": "Sell", - "titleCrypto": "Sell {{currency}}" + "titleCrypto": "Sell {{currency}}", + "exchangeDrawer": { + "completed": { + "title": "Transaction broadcast successfully", + "description": "Your operation has been sent to the network for confirmation. Please wait for your transaction to be confirmed and for the provider to process it." + } + } }, "receive": { "title": "Receive", @@ -6586,4 +6592,4 @@ "description": "Follow the instruction which appear on your Ledger’s Trusted Display." } } -} +} \ No newline at end of file