diff --git a/browser/ui/webui/brave_wallet/wallet_page_ui.cc b/browser/ui/webui/brave_wallet/wallet_page_ui.cc index f7333985acb5..a4dabb6307ee 100644 --- a/browser/ui/webui/brave_wallet/wallet_page_ui.cc +++ b/browser/ui/webui/brave_wallet/wallet_page_ui.cc @@ -60,6 +60,8 @@ WalletPageUI::WalletPageUI(content::WebUI* web_ui) plural_string_handler->AddLocalizedString( "braveWalletExchangeNamePlusSteps", IDS_BRAVE_WALLET_EXCHANGE_NAME_PLUS_STEPS); + plural_string_handler->AddLocalizedString( + "braveWalletPendingTransactions", IDS_BRAVE_WALLET_PENDING_TRANSACTIONS); web_ui->AddMessageHandler(std::move(plural_string_handler)); NavigationBarDataProvider::Initialize(source, profile); webui::SetupWebUIDataSource( diff --git a/browser/ui/webui/brave_wallet/wallet_panel_ui.cc b/browser/ui/webui/brave_wallet/wallet_panel_ui.cc index 4090e9e5763f..ceddcaf163b9 100644 --- a/browser/ui/webui/brave_wallet/wallet_panel_ui.cc +++ b/browser/ui/webui/brave_wallet/wallet_panel_ui.cc @@ -63,6 +63,8 @@ WalletPanelUI::WalletPanelUI(content::WebUI* web_ui) plural_string_handler->AddLocalizedString( "braveWalletExchangeNamePlusSteps", IDS_BRAVE_WALLET_EXCHANGE_NAME_PLUS_STEPS); + plural_string_handler->AddLocalizedString( + "braveWalletPendingTransactions", IDS_BRAVE_WALLET_PENDING_TRANSACTIONS); web_ui->AddMessageHandler(std::move(plural_string_handler)); webui::SetupWebUIDataSource(source, base::make_span(kBraveWalletPanelGenerated, diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index ab3911470240..c6dfba64b66e 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -1124,14 +1124,32 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletPortfolioSettings", IDS_BRAVE_WALLET_PORTFOLIO_SETTINGS}, {"braveWalletAccountFilterAllAccounts", IDS_BRAVE_WALLET_ACCOUNT_FILTER_ALL_ACCOUNTS}, + {"braveWalletGetHelp", IDS_BRAVE_WALLET_GET_HELP}, + {"braveWalletTransactionTakingLongTime", + IDS_BRAVE_WALLET_TRANSACTION_TAKING_LONG_TIME}, + {"braveWalletViewInActivity", IDS_BRAVE_WALLET_VIEW_IN_ACTIVITY}, + {"braveWalletSafelyDismissWindow", IDS_BRAVE_WALLET_SAFELY_DISMISS_WINDOW}, + {"braveWalletSendingAmountToAccount", + IDS_BRAVE_WALLET_SENDING_AMOUNT_TO_ACCOUNT}, + {"braveWalletAmountSentToAccount", IDS_BRAVE_WALLET_AMOUNT_SENT_TO_ACCOUNT}, + {"braveWalletSwappingAmountToAmountOnNetwork", + IDS_BRAVE_WALLET_SWAPPING_AMOUNT_TO_ACCOUNT_ON_NETWORK}, + {"braveWalletAmountAddedToAccount", + IDS_BRAVE_WALLET_AMOUNT_ADDED_TO_ACCOUNT}, + {"braveWalletBridgingAmountToNetwork", + IDS_BRAVE_WALLET_BRIDGING_AMOUNT_TO_NETWORK}, + {"braveWalletUnableToSendSwapOrBridge", + IDS_BRAVE_WALLET_UNABLE_TO_SEND_SWAP_OR_BRIDGE}, + {"braveWalletErrorAttemptingToTransact", + IDS_BRAVE_WALLET_ERROR_ATTEMPTING_TO_TRANSACT}, + {"braveWalletApprovingAmountOnExchange", + IDS_BRAVE_WALLET_APPROVING_AMOUNT_ON_EXCHANGE}, + {"braveWalletCancelTransactionDescription", + IDS_BRAVE_WALLET_CANCEL_TRANSACTION_DESCRIPTION}, {"braveWalletTransactionSubmittedTitle", IDS_BRAVE_WALLET_TRANSACTION_SUBMITTED_TITLE}, - {"braveWalletTransactionSubmittedDescription", - IDS_BRAVE_WALLET_TRANSACTION_SUBMITTED_DESCRIPTION}, {"braveWalletTransactionSignedTitle", IDS_BRAVE_WALLET_TRANSACTION_SIGNED_TITLE}, - {"braveWalletTransactionSignedDescription", - IDS_BRAVE_WALLET_TRANSACTION_SIGNED_DESCRIPTION}, {"braveWalletTransactionFailedHeaderTitle", IDS_BRAVE_WALLET_TRANSACTION_FAILED_HEADER_TITLE}, {"braveWalletTransactionFailedTitle", diff --git a/components/brave_wallet_ui/assets/svg-icons/error-circle-icon.svg b/components/brave_wallet_ui/assets/svg-icons/error-circle-icon.svg deleted file mode 100644 index 6116f0553598..000000000000 --- a/components/brave_wallet_ui/assets/svg-icons/error-circle-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.stories.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.stories.tsx new file mode 100644 index 000000000000..db59c6873078 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.stories.tsx @@ -0,0 +1,42 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' + +// Mocks +import { + mockTransactionInfo // +} from '../../../../stories/mock-data/mock-transaction-info' + +// Components +import { + WalletPanelStory // +} from '../../../../stories/wrappers/wallet-panel-story-wrapper' +import { CancelTransaction } from './cancel_transaction' + +// Styled Components +import { LongWrapper } from '../../../../stories/style' +import { PanelWrapper } from '../../../../panel/style' + +export const _CancelTransaction = { + render: () => { + return ( + + + + alert('Back clicked')} + transaction={mockTransactionInfo} + /> + + + + ) + } +} + +export default { component: CancelTransaction } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.style.ts new file mode 100644 index 000000000000..a3aa65c724d2 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.style.ts @@ -0,0 +1,11 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import styled from 'styled-components' +import Button from '@brave/leo/react/button' +import * as leo from '@brave/leo/tokens/css/variables' + +export const CancelButton = styled(Button)` + --leo-button-color: ${leo.color.button.errorBackground}; +` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.tsx new file mode 100644 index 000000000000..63b995558eb6 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/cancel_transaction/cancel_transaction.tsx @@ -0,0 +1,79 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' + +// Types +import { SerializableTransactionInfo } from '../../../../constants/types' + +// Utils +import { getLocale } from '$web-common/locale' +import { getCoinFromTxDataUnion } from '../../../../utils/network-utils' + +// Hooks +import { + useCancelTransactionMutation // +} from '../../../../common/slices/api.slice' + +// Components +import { PostConfirmationHeader } from '../common/post_confirmation_header' + +// Styled components +import { CancelButton } from './cancel_transaction.style' +import { Wrapper, Title } from '../common/common.style' +import { Column, Row, Text } from '../../../shared/style' + +interface Props { + transaction: SerializableTransactionInfo + onBack: () => void +} + +export const CancelTransaction = (props: Props) => { + const { transaction, onBack } = props + + // Computed + const txCoinType = getCoinFromTxDataUnion(transaction.txDataUnion) + + // Hooks + const [cancelTx] = useCancelTransactionMutation() + + // Methods + const onClickCancelTransaction = () => { + cancelTx({ + coinType: txCoinType, + chainId: transaction.chainId, + transactionId: transaction.id + }) + } + + return ( + + + + {getLocale('braveWalletTransactionCancel')} + + {getLocale('braveWalletCancelTransactionDescription')} + + + + + {getLocale('braveWalletTransactionCancel')} + + + + ) +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/common/common.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/common/common.style.ts index 1edde1c73e5c..c3109d5393f5 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/common/common.style.ts +++ b/components/brave_wallet_ui/components/extension/post-confirmation/common/common.style.ts @@ -3,26 +3,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at https://mozilla.org/MPL/2.0/. import styled from 'styled-components' - +import ProgressRing from '@brave/leo/react/progressRing' +import LeoIcon from '@brave/leo/react/icon' +import LeoAlert from '@brave/leo/react/alert' +import * as leo from '@brave/leo/tokens/css/variables' import LinkSvg from '../../../../assets/svg-icons/link-icon.svg' import LoadingIcon from '../../../../assets/svg-icons/loading-slow.svg' -import { WalletButton } from '../../../shared/style' +import { WalletButton, Column, Text } from '../../../shared/style' -export const TransactionStatusIcon = styled.div` - width: 112px; - height: 112px; - margin: 24px 0; +export const Wrapper = styled(Column)` + background-color: ${leo.color.container.highlight}; ` -export const TransactionStatusText = styled.div` - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-size: 18px; - line-height: 27px; - - text-align: center; - letter-spacing: 0.02em; +export const Title = styled(Text)` + font: ${leo.font.heading.h3}; + margin-bottom: 8px; ` export const TransactionStatusDescription = styled.div` @@ -33,31 +28,7 @@ export const TransactionStatusDescription = styled.div` line-height: 20px; text-align: center; color: ${(p) => p.theme.color.text02}; - padding: 8px 16px; - flex-grow: 1; -` - -export const PendingTransactionsRow = styled.div` - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-size: 12px; - line-height: 20px; - text-align: center; - color: ${(p) => p.theme.color.text03}; - padding: 8px 16px; - flex-grow: 1; -` - -export const ButtonRow = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: row; - width: 100%; - padding-bottom: 22px; - gap: 8px; ` export const LinkIcon = styled.div` @@ -90,3 +61,68 @@ export const Loader = styled.div` margin: 36px 0; opacity: 0.4; ` + +export const LoadingRing = styled(ProgressRing)` + --leo-progressring-size: 110px; + --leo-progressring-color: ${leo.color.icon.interactive}; + margin-bottom: 46px; +` + +export const StatusIcon = styled(LeoIcon)` + --leo-icon-size: 34px; + color: ${leo.color.icon.interactive}; +` + +export const Button = styled(WalletButton)` + cursor: pointer; + background-color: none; + background: none; + outline: none; + border: none; + padding: 0px; + margin: 0px; +` + +export const HeaderButton = styled(Button)` + margin: 2px 0px; +` + +export const HeaderIcon = styled(LeoIcon)` + --leo-icon-size: 24px; + color: ${leo.color.icon.default}; +` + +export const ExplorerIcon = styled(LeoIcon).attrs({ + name: 'arrow-diagonal-up-right' +})` + --leo-icon-size: 20px; + color: ${leo.color.icon.interactive}; +` + +export const Alert = styled(LeoAlert)` + width: 100%; + --leo-alert-padding: 12px; + margin-bottom: 46px; +` + +export const ErrorOrSuccessIconWrapper = styled.div<{ + kind: 'error' | 'success' +}>` + padding: 20px; + border-radius: 100%; + background-color: ${(p) => + p.kind === 'error' + ? leo.color.systemfeedback.errorBackground + : leo.color.systemfeedback.successBackground}; + margin-bottom: 40px; +` + +export const ErrorOrSuccessIcon = styled(LeoIcon)<{ + kind: 'error' | 'success' +}>` + --leo-icon-size: 60px; + color: ${(p) => + p.kind === 'error' + ? leo.color.systemfeedback.errorIcon + : leo.color.systemfeedback.successIcon}; +` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/common/post_confirmation_header.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/common/post_confirmation_header.tsx new file mode 100644 index 000000000000..ae5485cdacf1 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/common/post_confirmation_header.tsx @@ -0,0 +1,56 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' +import Icon from '@brave/leo/react/icon' +import LeoButton from '@brave/leo/react/button' + +// Utils +import { getLocale } from '$web-common/locale' + +// Styled Components +import { HeaderButton, HeaderIcon } from './common.style' +import { Row } from '../../../shared/style' + +interface Props { + onClose?: () => void + onBack?: () => void + showHelp?: boolean +} + +export const PostConfirmationHeader = (props: Props) => { + const { onClose, onBack, showHelp } = props + + return ( + + {showHelp && ( +
+ + + {getLocale('braveWalletGetHelp')} + +
+ )} + {onBack && ( + + + + )} + {onClose && ( + + + + )} +
+ ) +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/common/speed_up_alert.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/common/speed_up_alert.tsx new file mode 100644 index 000000000000..d6ad9cec74d7 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/common/speed_up_alert.tsx @@ -0,0 +1,38 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' +import Button from '@brave/leo/react/button' + +// Utils +import { getLocale } from '$web-common/locale' + +// Styled Components +import { Alert } from './common.style' + +interface Props { + onSpeedUp?: () => void +} + +export const SpeedUpAlert = (props: Props) => { + const { onSpeedUp } = props + + return ( + + {getLocale('braveWalletTransactionTakingLongTime')} +
+ +
+
+ ) +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx new file mode 100644 index 000000000000..d4acae7f4cb5 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx @@ -0,0 +1,329 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' +import { skipToken } from '@reduxjs/toolkit/query/react' + +// Types +import { + BraveWallet, + SerializableTransactionInfo // +} from '../../../../constants/types' + +// Utils +import { getLocale, splitStringForTag } from '$web-common/locale' +import { + findTransactionToken, + getFormattedTransactionTransferredValue, + getIsTxApprovalUnlimited, + getTransactionApprovalTargetAddress, + getTransactionToAddress, + isBridgeTransaction, + isSwapTransaction +} from '../../../../utils/tx-utils' +import { getCoinFromTxDataUnion } from '../../../../utils/network-utils' +import { getAddressLabel } from '../../../../utils/account-utils' +import Amount from '../../../../utils/amount' + +// Queries +import { + useAccountQuery, + useGetCombinedTokensListQuery +} from '../../../../common/slices/api.slice.extra' +import { + useGetAccountInfosRegistryQuery, + useGetNetworkQuery +} from '../../../../common/slices/api.slice' +import { + accountInfoEntityAdaptorInitialState // +} from '../../../../common/slices/entities/account-info.entity' + +// Hooks +import { + useTransactionsNetwork // +} from '../../../../common/hooks/use-transactions-network' +import { + useSwapTransactionParser // +} from '../../../../common/hooks/use-swap-tx-parser' +import { useExplorer } from '../../../../common/hooks/explorer' + +// Styled Components +import { Button, ExplorerIcon } from './common.style' +import { Row, Text } from '../../../shared/style' + +interface Props { + transaction: SerializableTransactionInfo +} + +export const TransactionIntent = (props: Props) => { + const { transaction } = props + + // Computed + const isBridge = isBridgeTransaction(transaction) + const isSwap = isSwapTransaction(transaction) + const isSwapOrBridge = isBridge || isSwap + const isERC20Approval = + transaction.txType === BraveWallet.TransactionType.ERC20Approve + const isTxApprovalUnlimited = getIsTxApprovalUnlimited(transaction) + const txApprovalTarget = getTransactionApprovalTargetAddress(transaction) + const txCoinType = getCoinFromTxDataUnion(transaction.txDataUnion) + const txToAddress = getTransactionToAddress(transaction) + + // Queries + const { account: txAccount } = useAccountQuery(transaction.fromAccountId) + + // Custom Hooks + const transactionNetwork = useTransactionsNetwork(transaction) + const onClickViewOnBlockExplorer = useExplorer(transactionNetwork) + + const { normalizedTransferredValue } = + getFormattedTransactionTransferredValue({ + tx: transaction, + txAccount, + txNetwork: transactionNetwork + }) + + const { data: txNetwork } = useGetNetworkQuery({ + chainId: transaction.chainId, + coin: txCoinType + }) + + const { data: bridgeToNetwork } = useGetNetworkQuery( + isBridge && + transaction.swapInfo?.toChainId && + transaction.swapInfo.toCoin !== undefined + ? { + chainId: transaction.swapInfo.toChainId, + coin: transaction.swapInfo.toCoin + } + : skipToken + ) + + const { data: accountInfosRegistry = accountInfoEntityAdaptorInitialState } = + useGetAccountInfosRegistryQuery(undefined) + + const { buyToken, sellToken, buyAmountWei, sellAmountWei } = + useSwapTransactionParser(transaction) + + const { data: combinedTokensList } = useGetCombinedTokensListQuery() + + const formattedSellAmount = sellToken + ? sellAmountWei + .divideByDecimals(sellToken.decimals) + .formatAsAsset(6, sellToken.symbol) + : '' + const formattedBuyAmount = buyToken + ? buyAmountWei + .divideByDecimals(buyToken.decimals) + .formatAsAsset(6, buyToken.symbol) + : '' + + const recipientLabel = getAddressLabel( + isERC20Approval ? txApprovalTarget : txToAddress, + accountInfosRegistry + ) + + const transactionsToken = findTransactionToken( + transaction, + combinedTokensList + ) + + const formattedSendAmount = React.useMemo(() => { + if (!transactionsToken) { + return '' + } + if ( + transactionsToken.isErc721 || + transactionsToken.isErc1155 || + transactionsToken.isNft + ) { + return `${transactionsToken.name} ${transactionsToken.symbol}` + } + return new Amount(normalizedTransferredValue).formatAsAsset( + 6, + transactionsToken.symbol + ) + }, [transactionsToken, normalizedTransferredValue]) + + const formattedApprovalAmount = isTxApprovalUnlimited + ? `${getLocale('braveWalletTransactionApproveUnlimited')} ${ + transactionsToken?.symbol ?? '' + }` + : formattedSendAmount + + const transactionFailed = + transaction.txStatus === BraveWallet.TransactionStatus.Dropped || + transaction.txStatus === BraveWallet.TransactionStatus.Error + + const transactionConfirmed = + transaction.txStatus === BraveWallet.TransactionStatus.Confirmed + + const firstDuringValue = React.useMemo(() => { + if (isERC20Approval) { + return formattedApprovalAmount + } + if (isSwapOrBridge && transactionConfirmed) { + return formattedBuyAmount + } + if (isSwapOrBridge) { + return formattedSellAmount + } + return formattedSendAmount + }, [ + isERC20Approval, + formattedApprovalAmount, + isSwapOrBridge, + transactionConfirmed, + formattedBuyAmount, + formattedSellAmount, + formattedSendAmount + ]) + + const secondDuringValue = React.useMemo(() => { + if (isSwapOrBridge && transactionConfirmed) { + return recipientLabel + } + if (isBridge) { + return bridgeToNetwork?.chainName ?? '' + } + if (isSwap) { + return formattedBuyAmount + } + return recipientLabel + }, [ + isSwapOrBridge, + transactionConfirmed, + recipientLabel, + isBridge, + bridgeToNetwork, + isSwap, + formattedBuyAmount + ]) + + const sendSwapOrBridgeLocale = React.useMemo(() => { + if (isBridge) { + return getLocale('braveWalletBridge').toLocaleLowerCase() + } + if (isSwap) { + return getLocale('braveWalletSwap').toLocaleLowerCase() + } + return getLocale('braveWalletSend').toLocaleLowerCase() + }, [isBridge, isSwap]) + + const descriptionLocale = React.useMemo(() => { + if (transactionFailed) { + return 'braveWalletErrorAttemptingToTransact' + } + if (isSwapOrBridge && transactionConfirmed) { + return 'braveWalletAmountAddedToAccount' + } + if (isBridge) { + return 'braveWalletBridgingAmountToNetwork' + } + if (isSwap) { + return 'braveWalletSwappingAmountToAmountOnNetwork' + } + if (isERC20Approval) { + return 'braveWalletApprovingAmountOnExchange' + } + if (transactionConfirmed) { + return 'braveWalletAmountSentToAccount' + } + return 'braveWalletSendingAmountToAccount' + }, [ + transactionFailed, + isSwapOrBridge, + transactionConfirmed, + isBridge, + isSwap, + isERC20Approval + ]) + + const descriptionString = getLocale(descriptionLocale) + .replace('$5', firstDuringValue) + .replace('$6', secondDuringValue) + .replace( + '$7', + transactionFailed ? sendSwapOrBridgeLocale : txNetwork?.chainName ?? '' + ) + + const { + beforeTag: firstBefore, + duringTag: firstDuring, + afterTag: firstAfter + } = splitStringForTag(descriptionString, 1) + const { + beforeTag: secondBefore, + duringTag: secondDuring, + afterTag: secondAfter + } = splitStringForTag(firstAfter ?? '', 3) + + return ( + + {firstBefore && ( + + {firstBefore} + + )} + {firstDuring && ( + + {firstDuring} + + )} + {secondBefore && ( + + {secondBefore} + + )} + {secondDuring && ( + + + {secondDuring} + + + + )} + {secondAfter && ( + + {secondAfter} + + )} + + ) +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.stories.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.stories.tsx index 17db35272cad..68cad02df89e 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.stories.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.stories.tsx @@ -2,40 +2,66 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at https://mozilla.org/MPL/2.0/. - import * as React from 'react' -// Utils -import { getLocale } from '$web-common/locale' +// Types +import { + BraveWallet, + StorybookTransactionArgs, + StorybookTransactionOptions +} from '../../../../constants/types' + +// Mocks +import { + getPostConfirmationStatusMockTransaction // +} from '../../../../stories/mock-data/mock-transaction-info' // Components -import WalletPanelStory from '../../../../stories/wrappers/wallet-panel-story-wrapper' -import { StyledExtensionWrapper } from '../../../../stories/style' -import { PanelWrapper } from '../../../../panel/style' +import { + WalletPanelStory // +} from '../../../../stories/wrappers/wallet-panel-story-wrapper' import { TransactionComplete } from './complete' +// Styled Components +import { LongWrapper } from '../../../../stories/style' +import { PanelWrapper } from '../../../../panel/style' + export const _TransactionComplete = { - render: () => { - const onClose = () => alert('Close panel screen') + render: (args: StorybookTransactionArgs) => { + // Props + const { transactionType } = args + + // Computed + const transaction = getPostConfirmationStatusMockTransaction( + transactionType, + BraveWallet.TransactionStatus.Confirmed + ) return ( - - + + alert('Clicked primary CTA')} - onClickSecondaryCTA={() => alert('Clicked secondary CTA')} + transaction={transaction} + onClose={() => alert('Close panel screen clicked.')} + onClickViewInActivity={() => alert('View in activity clicked.')} /> - + ) } } -export default { component: TransactionComplete } +export default { + component: TransactionComplete, + argTypes: { + transactionType: { + options: StorybookTransactionOptions, + control: { type: 'select' } + } + } +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.style.ts deleted file mode 100644 index 480c4ed92935..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.style.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import styled from 'styled-components' - -import SuccessSvg from '../../../../assets/svg-icons/success-circle-icon.svg' -import { - TransactionStatusIcon, - TransactionStatusText -} from '../common/common.style' - -export const SuccessIcon = styled(TransactionStatusIcon)` - background: url(${SuccessSvg}); -` - -export const Title = styled(TransactionStatusText)` - color: ${(p) => p.theme.color.text01}; -` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.tsx index 159afb1b663c..013c89b12643 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/complete/complete.tsx @@ -3,75 +3,106 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at https://mozilla.org/MPL/2.0/. import * as React from 'react' +import LeoButton from '@brave/leo/react/button' +import { + PluralStringProxyImpl // +} from 'chrome://resources/js/plural_string_proxy.js' +import usePromise from '$web-common/usePromise' // Utils import { getLocale } from '$web-common/locale' // Hooks -import { usePendingTransactions } from '../../../../common/hooks/use-pending-transaction' +import { + usePendingTransactions // +} from '../../../../common/hooks/use-pending-transaction' // Components -import { NavButton } from '../../buttons/nav-button/index' -import { Panel } from '../../panel/index' -import { SuccessIcon, Title } from './complete.style' import { - ButtonRow, - PendingTransactionsRow, - TransactionStatusDescription + PostConfirmationHeader // +} from '../common/post_confirmation_header' + +// Styled Components +import { + // TransactionStatusDescription, + Title, + Wrapper, + ErrorOrSuccessIconWrapper, + ErrorOrSuccessIcon } from '../common/common.style' +import { Column, Row, Text } from '../../../shared/style' +import { SerializableTransactionInfo } from '../../../../constants/types' +import { TransactionIntent } from '../common/transaction_intent' interface Props { - headerTitle: string - description: string - isPrimaryCTADisabled: boolean - primaryCTAText: string + transaction: SerializableTransactionInfo onClose: () => void - onClickSecondaryCTA: () => void - onClickPrimaryCTA: () => void + onClickViewInActivity: () => void } export const TransactionComplete = (props: Props) => { - const { - headerTitle, - description, - isPrimaryCTADisabled, - primaryCTAText, - onClose, - onClickPrimaryCTA, - onClickSecondaryCTA - } = props + const { transaction, onClose, onClickViewInActivity } = props + // Hooks const { transactionsQueueLength } = usePendingTransactions() + // Computed + const { result: pendingTransactionsLocale } = usePromise( + async () => + PluralStringProxyImpl.getInstance().getPluralString( + 'braveWalletPendingTransactions', + transactionsQueueLength + ), + [transactionsQueueLength] + ) + const hasMoreTransactions = transactionsQueueLength >= 1 + return ( - - - {getLocale('braveWalletTransactionCompleteTitle')} - {description} - - {transactionsQueueLength >= 1 && ( - - {transactionsQueueLength} more transactions pending. - - )} - - - - - - + + + + + + {getLocale('braveWalletTransactionCompleteTitle')} + + + + {hasMoreTransactions && ( + + {pendingTransactionsLocale} + + )} + + + {getLocale('braveWalletViewInActivity')} + + {hasMoreTransactions && ( + + {getLocale('braveWalletButtonNext')} + + )} + + + ) } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/complete/index.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/complete/index.tsx deleted file mode 100644 index 8ec6f9f99477..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/complete/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import { TransactionComplete } from './complete' - -export { TransactionComplete } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.stories.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.stories.tsx deleted file mode 100644 index e8a366b97749..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.stories.tsx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. - -import * as React from 'react' - -// Utils -import { getLocale } from '$web-common/locale' - -// Components -import WalletPanelStory from '../../../../stories/wrappers/wallet-panel-story-wrapper' -import { StyledExtensionWrapper } from '../../../../stories/style' -import { PanelWrapper } from '../../../../panel/style' -import { TransactionFailed } from './failed' - -export const _TransactionFailed = { - render: () => { - const onClose = () => alert('Close panel screen') - - return ( - - - - alert('Clicked primary CTA')} - /> - - - - ) - } -} - -export default { component: TransactionFailed } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.style.ts deleted file mode 100644 index 5751eba4d1aa..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.style.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import styled from 'styled-components' - -import ErrorSvg from '../../../../assets/svg-icons/error-circle-icon.svg' -import { - TransactionStatusIcon, - TransactionStatusText -} from '../common/common.style' - -export const ErrorIcon = styled(TransactionStatusIcon)` - background: url(${ErrorSvg}); -` - -export const Title = styled(TransactionStatusText)` - color: ${(p) => p.theme.color.errorBorder}; -` - -export const ErrorDetailTitle = styled.div` - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-size: 12px; - line-height: 18px; - color: ${(p) => p.theme.color.errorBorder}; - margin: 0 16px; -` - -export const ErrorDetailContentContainer = styled.div` - background: ${(p) => p.theme.color.errorBackground}; - border-radius: 8px; - margin: 16px; -` - -export const ErrorDetailContent = styled.div` - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-size: 12px; - line-height: 18px; - color: ${(p) => p.theme.color.text01}; - opacity: 0.9; - margin: 8px; -` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.tsx deleted file mode 100644 index 88a8c7fae5c4..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/failed/failed.tsx +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import * as React from 'react' - -import { getLocale } from '$web-common/locale' - -import { Panel } from '../../panel/index' -import { NavButton } from '../../buttons/nav-button/index' -import { - ErrorIcon, - Title, - ErrorDetailTitle, - ErrorDetailContent, - ErrorDetailContentContainer -} from './failed.style' -import { ButtonRow, TransactionStatusDescription } from '../common/common.style' -import { PopupModal } from '../../popup-modals' - -interface Props { - headerTitle: string - isPrimaryCTADisabled: boolean - customDescription: string | undefined - errorDetailTitle: string - errorDetailContent?: string | undefined - onClose: () => void - onClickPrimaryCTA: () => void -} - -export const TransactionFailed = (props: Props) => { - const { - headerTitle, - errorDetailTitle, - errorDetailContent, - isPrimaryCTADisabled, - customDescription, - onClose, - onClickPrimaryCTA - } = props - - const [showErrorCodeModal, setShowErrorCodeModal] = - React.useState(false) - - const description = customDescription || - getLocale('braveWalletTransactionFailedDescription') - - return ( - - - {getLocale('braveWalletTransactionFailedTitle')} - - {description} - - - {errorDetailContent && ( - setShowErrorCodeModal(true)} - /> - )} - - - - {showErrorCodeModal && errorDetailContent && ( - setShowErrorCodeModal(false)} - /> - )} - - ) -} - -interface ModalProps { - title: string - content: string - onClose: () => void -} - -const ErrorDetail = (props: ModalProps) => { - const { content, title, onClose } = props - - return ( - - {title} - - - {content} - - - - - - - ) -} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed/index.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/failed/index.tsx deleted file mode 100644 index 3d68c9055646..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/failed/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import { TransactionFailed } from './failed' - -export { TransactionFailed } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.stories.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.stories.tsx new file mode 100644 index 000000000000..10e030e052e9 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.stories.tsx @@ -0,0 +1,66 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' + +// Types +import { + BraveWallet, + StorybookTransactionArgs, + StorybookTransactionOptions +} from '../../../../constants/types' + +// Mocks +import { + getPostConfirmationStatusMockTransaction // +} from '../../../../stories/mock-data/mock-transaction-info' + +// Components +import { + WalletPanelStory // +} from '../../../../stories/wrappers/wallet-panel-story-wrapper' +import { TransactionFailedOrCanceled } from './failed_or_canceled' + +// Styled Components +import { LongWrapper } from '../../../../stories/style' +import { PanelWrapper } from '../../../../panel/style' + +export const _TransactionFailedOrCanceled = { + render: (args: StorybookTransactionArgs) => { + // Props + const { transactionType } = args + + // Computed + const transaction = getPostConfirmationStatusMockTransaction( + transactionType, + BraveWallet.TransactionStatus.Error + ) + + return ( + + + + alert('Close panel screen clicked.')} + /> + + + + ) + } +} + +export default { + component: TransactionFailedOrCanceled, + argTypes: { + transactionType: { + options: StorybookTransactionOptions, + control: { type: 'select' } + } + } +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.tsx new file mode 100644 index 000000000000..cb7020bb3d0a --- /dev/null +++ b/components/brave_wallet_ui/components/extension/post-confirmation/failed_or_canceled/failed_or_canceled.tsx @@ -0,0 +1,153 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import * as React from 'react' +import LeoButton from '@brave/leo/react/button' + +// Hooks +import { + useRetryTransactionMutation // +} from '../../../../common/slices/api.slice' + +// Selectors +import { + useUnsafeUISelector // +} from '../../../../common/hooks/use-safe-selector' +import { UISelectors } from '../../../../common/selectors' + +// Types +import { + BraveWallet, + SerializableTransactionInfo // +} from '../../../../constants/types' + +// Utils +import { getLocale } from '$web-common/locale' +import { + isBridgeTransaction, + isSwapTransaction +} from '../../../../utils/tx-utils' +import { getCoinFromTxDataUnion } from '../../../../utils/network-utils' + +// Components +import { + PostConfirmationHeader // +} from '../common/post_confirmation_header' +import { TransactionIntent } from '../common/transaction_intent' + +// Styled Components +import { + // TransactionStatusDescription, + Title, + Wrapper, + ErrorOrSuccessIconWrapper, + ErrorOrSuccessIcon +} from '../common/common.style' +import { Column, Row, Text } from '../../../shared/style' + +interface Props { + transaction: SerializableTransactionInfo + onClose: () => void +} + +export const TransactionFailedOrCanceled = (props: Props) => { + const { transaction, onClose } = props + + // redux + const transactionProviderErrorRegistry = useUnsafeUISelector( + UISelectors.transactionProviderErrorRegistry + ) + + // Hooks + const [retryTx] = useRetryTransactionMutation() + + // Computed + const txCoinType = getCoinFromTxDataUnion(transaction.txDataUnion) + const isBridge = isBridgeTransaction(transaction) + const isSwap = isSwapTransaction(transaction) + + const providerError = transactionProviderErrorRegistry[transaction.id] + const errorCode = + providerError?.code.providerError ?? + providerError?.code.zcashProviderError ?? + providerError?.code.bitcoinProviderError ?? + providerError?.code.solanaProviderError ?? + providerError?.code.filecoinProviderError ?? + BraveWallet.ProviderError.kUnknown + + const errorDetails = providerError && `${errorCode}: ${providerError.message}` + + // Memos + const sendSwapOrBridgeLocale = React.useMemo(() => { + if (isBridge) { + return 'braveWalletBridge' + } + if (isSwap) { + return 'braveWalletSwap' + } + return 'braveWalletSend' + }, [isBridge, isSwap]) + + // Methods + const onClickRetryTransaction = () => { + retryTx({ + coinType: txCoinType, + chainId: transaction.chainId, + transactionId: transaction.id + }) + } + + return ( + + + + + + + + {getLocale('braveWalletUnableToSendSwapOrBridge').replace( + '$1', + getLocale(sendSwapOrBridgeLocale).toLocaleLowerCase() + )} + + + + {errorDetails} + + + + + {getLocale('braveWalletButtonClose')} + + + {getLocale('braveWalletButtonRetry')} + + + + ) +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx index b8d21d65b185..7cdd8120ea71 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx @@ -11,38 +11,31 @@ import { useHistory } from 'react-router' import { BraveWallet, TransactionInfoLookup } from '../../../constants/types' // Utils -import { getLocale } from '$web-common/locale' -import { - findTransactionToken, - getFormattedTransactionTransferredValue, - getTransactionErc721TokenId, - getTransactionIntent -} from '../../../utils/tx-utils' import { getCoinFromTxDataUnion } from '../../../utils/network-utils' import { makeTransactionDetailsRoute } from '../../../utils/routes-utils' // Hooks -import { useTransactionsNetwork } from '../../../common/hooks/use-transactions-network' -import { usePendingTransactions } from '../../../common/hooks/use-pending-transaction' import { useGetTransactionQuery } from '../../../common/slices/api.slice' -import { useUnsafeUISelector } from '../../../common/hooks/use-safe-selector' -import { - useAccountQuery, - useGetCombinedTokensListQuery -} from '../../../common/slices/api.slice.extra' -import { useSwapTransactionParser } from '../../../common/hooks/use-swap-tx-parser' // Actions import * as WalletPanelActions from '../../../panel/actions/wallet_panel_actions' // Components -import { Panel } from '../panel/index' -import { TransactionSubmittedOrSigned } from './submitted_or_signed' -import { TransactionComplete } from './complete' -import { TransactionFailed } from './failed' +import { + TransactionSubmittedOrSigned // +} from './submitted_or_signed/submitted_or_signed' +import { TransactionComplete } from './complete/complete' +import { + TransactionFailedOrCanceled // +} from './failed_or_canceled/failed_or_canceled' +import { + CancelTransaction // +} from './cancel_transaction/cancel_transaction' + +// Styled Components import { Loader } from './common/common.style' import { Skeleton } from '../../shared/loading-skeleton/styles' -import { UISelectors } from '../../../common/selectors' +import { Column } from '../../shared/style' interface Props { transactionLookup: TransactionInfoLookup @@ -52,72 +45,18 @@ export function TransactionStatus({ transactionLookup }: Props) { // history const history = useHistory() - // redux - const transactionProviderErrorRegistry = useUnsafeUISelector( - UISelectors.transactionProviderErrorRegistry - ) - // queries const { data: tx } = useGetTransactionQuery(transactionLookup ?? skipToken) - const { account: txAccount } = useAccountQuery(tx?.fromAccountId) - const { data: combinedTokensList } = useGetCombinedTokensListQuery() + // State + const [showCancelTransaction, setShowCancelTransaction] = + React.useState(false) // hooks const dispatch = useDispatch() - const transactionNetwork = useTransactionsNetwork(tx || undefined) - const { transactionsQueueLength } = usePendingTransactions() - - const { buyToken, sellToken, buyAmountWei, sellAmountWei } = - useSwapTransactionParser(tx ?? undefined) - - const transactionIntent = React.useMemo(() => { - if (!tx) { - return '' - } - - const token = findTransactionToken(tx, combinedTokensList) - - const { normalizedTransferredValue } = - getFormattedTransactionTransferredValue({ - tx, - txAccount, - txNetwork: transactionNetwork, - token, - sellToken - }) - - const buyAmount = buyToken - ? buyAmountWei.divideByDecimals(buyToken.decimals) - : undefined - const sellAmount = sellToken - ? sellAmountWei.divideByDecimals(sellToken.decimals) - : undefined - - return getTransactionIntent({ - tx, - normalizedTransferredValue, - buyAmount, - buyToken, - erc721TokenId: getTransactionErc721TokenId(tx), - sellAmount, - sellToken, - token, - transactionNetwork - }) - }, [ - tx, - combinedTokensList, - txAccount, - transactionNetwork, - buyToken, - sellToken, - buyAmountWei, - sellAmountWei - ]) // methods - const viewTransactionDetail = React.useCallback(() => { + const onClickViewInActivity = React.useCallback(() => { if (!tx?.id) { return } @@ -134,25 +73,31 @@ export function TransactionStatus({ transactionLookup }: Props) { const onClose = () => dispatch(WalletPanelActions.setSelectedTransactionId(undefined)) - const completePrimaryCTAText = - transactionsQueueLength === 0 - ? getLocale('braveWalletButtonClose') - : getLocale('braveWalletButtonNext') // render if (!tx) { return } + if (showCancelTransaction) { + return ( + setShowCancelTransaction(false)} + transaction={tx} + /> + ) + } + if ( tx.txStatus === BraveWallet.TransactionStatus.Submitted || tx.txStatus === BraveWallet.TransactionStatus.Signed ) { return ( setShowCancelTransaction(true)} + onClickViewInActivity={onClickViewInActivity} /> ) } @@ -160,57 +105,28 @@ export function TransactionStatus({ transactionLookup }: Props) { if (tx.txStatus === BraveWallet.TransactionStatus.Confirmed) { return ( ) } if (tx.txStatus === BraveWallet.TransactionStatus.Error) { - const providerError = transactionProviderErrorRegistry[tx.id] - const errorCode = - providerError?.code.providerError ?? - providerError?.code.zcashProviderError ?? - providerError?.code.bitcoinProviderError ?? - providerError?.code.solanaProviderError ?? - providerError?.code.filecoinProviderError ?? - BraveWallet.ProviderError.kUnknown - - const errorDetailContent = - providerError && `${errorCode}: ${providerError.message}` - const customDescription = - errorCode === - BraveWallet.ZCashProviderError.kMultipleTransactionsNotSupported - ? providerError.message - : undefined - return ( - ) } return ( - - + ) } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/index.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/index.tsx deleted file mode 100644 index 217a1361317d..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. -import { TransactionSubmittedOrSigned } from './submitted_or_signed' - -export { TransactionSubmittedOrSigned } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.stories.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.stories.tsx index 4cddf361459d..3b4826a6a4db 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.stories.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.stories.tsx @@ -2,36 +2,69 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at https://mozilla.org/MPL/2.0/. - import * as React from 'react' +// Types +import { + BraveWallet, + StorybookTransactionArgs, + StorybookTransactionOptions +} from '../../../../constants/types' + // Mocks -import { mockTransactionInfo } from '../../../../stories/mock-data/mock-transaction-info' +import { + getPostConfirmationStatusMockTransaction // +} from '../../../../stories/mock-data/mock-transaction-info' // Components -import WalletPanelStory from '../../../../stories/wrappers/wallet-panel-story-wrapper' -import { StyledExtensionWrapper } from '../../../../stories/style' -import { PanelWrapper } from '../../../../panel/style' +import { + WalletPanelStory // +} from '../../../../stories/wrappers/wallet-panel-story-wrapper' import { TransactionSubmittedOrSigned } from './submitted_or_signed' +// Styled Components +import { LongWrapper } from '../../../../stories/style' +import { PanelWrapper } from '../../../../panel/style' + export const _TransactionSubmittedOrSigned = { - render: () => { - const onClose = () => alert('Close panel screen') + render: (args: StorybookTransactionArgs) => { + // Props + const { transactionType } = args + + // Computed + const transaction = getPostConfirmationStatusMockTransaction( + transactionType, + BraveWallet.TransactionStatus.Submitted + ) return ( - - + + alert('Close panel screen clicked.')} + onShowCancelTransaction={() => + alert('Show cancel transaction clicked.') + } + transaction={transaction} + onClickViewInActivity={() => alert('View in activity clicked.')} /> - + ) } } -export default { component: TransactionSubmittedOrSigned } +export default { + component: TransactionSubmittedOrSigned, + argTypes: { + transactionType: { + options: StorybookTransactionOptions, + control: { type: 'select' } + } + } +} diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts deleted file mode 100644 index 6c1f709ada52..000000000000 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -import styled from 'styled-components' - -import SubmittedOrSignedSvg from '../../../../assets/svg-icons/submitted-circle-icon.svg' -import { WalletButton } from '../../../shared/style' -import { - TransactionStatusIcon, - TransactionStatusText -} from '../common/common.style' - -export const SubmittedOrSignedIcon = styled(TransactionStatusIcon)` - background: url(${SubmittedOrSignedSvg}); - background-size: contain; -` - -export const Title = styled(TransactionStatusText)` - color: ${(p) => p.theme.color.text01}; -` - -export const DetailButton = styled(WalletButton)` - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-size: 12px; - line-height: 20px; - text-align: center; - color: ${(p) => p.theme.color.interactive05}; - background: none; - cursor: pointer; - outline: none; - border: none; - margin: 0; - padding: 0; -` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx index 2da85b62e2a8..26ca9516c948 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx @@ -3,6 +3,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // you can obtain one at https://mozilla.org/MPL/2.0/. import * as React from 'react' +import LeoButton from '@brave/leo/react/button' // Constants import { @@ -10,65 +11,124 @@ import { SerializableTransactionInfo } from '../../../../constants/types' -// Hooks -import { useExplorer } from '../../../../common/hooks/explorer' -import { useTransactionsNetwork } from '../../../../common/hooks/use-transactions-network' - // Utils import { getLocale } from '$web-common/locale' +// Components +import { + PostConfirmationHeader // +} from '../common/post_confirmation_header' +import { SpeedUpAlert } from '../common/speed_up_alert' +import { TransactionIntent } from '../common/transaction_intent' + // Styled components -import { Panel } from '../../panel/index' -import { SubmittedOrSignedIcon, Title } from './submitted_or_signed.style' +import { LoadingRing, StatusIcon, Wrapper, Title } from '../common/common.style' +import { Column, Row, Text, VerticalSpace } from '../../../shared/style' import { - ButtonRow, - DetailButton, - LinkIcon, - TransactionStatusDescription -} from '../common/common.style' + isBridgeTransaction, + isSwapTransaction +} from '../../../../utils/tx-utils' interface Props { - headerTitle: string transaction: SerializableTransactionInfo onClose: () => void + onShowCancelTransaction: () => void + onClickViewInActivity: () => void } export const TransactionSubmittedOrSigned = (props: Props) => { - const { headerTitle, transaction, onClose } = props + const { + transaction, + onClose, + onShowCancelTransaction, + onClickViewInActivity + } = props - // custom hooks - const transactionNetwork = useTransactionsNetwork(transaction) - const onClickViewOnBlockExplorer = useExplorer(transactionNetwork) + // State + const [showSpeedUpAlert, setShowSpeedupAlert] = React.useState(false) - const title = - transaction.txStatus === BraveWallet.TransactionStatus.Submitted - ? getLocale('braveWalletTransactionSubmittedTitle') - : getLocale('braveWalletTransactionSignedTitle') - const description = - transaction.txStatus === BraveWallet.TransactionStatus.Submitted - ? getLocale('braveWalletTransactionSubmittedDescription') - : getLocale('braveWalletTransactionSignedDescription') + // Computed + const isBridge = isBridgeTransaction(transaction) + const isSwap = isSwapTransaction(transaction) + const isERC20Approval = + transaction.txType === BraveWallet.TransactionType.ERC20Approve + + // Memos + const statusIconName = React.useMemo(() => { + if (isERC20Approval) { + return 'lock-open' + } + if (isBridge) { + return 'web3-bridge' + } + if (isSwap) { + return 'swap-horizontal' + } + return 'send-filled' + }, [isERC20Approval, isBridge, isSwap]) + + React.useEffect(() => { + const timeId = setTimeout(() => { + setShowSpeedupAlert(true) + }, 5000) + return () => { + clearTimeout(timeId) + } + }, []) return ( - - - {title} - {description} - - + + + {showSpeedUpAlert ? ( + + ) : ( + )} + + + + + {transaction.txStatus === BraveWallet.TransactionStatus.Submitted + ? getLocale('braveWalletTransactionSubmittedTitle') + : getLocale('braveWalletTransactionSignedTitle')} + + + + + + - {getLocale('braveWalletTransactionExplorer')} - - - - + {getLocale('braveWalletSafelyDismissWindow')} + + + + {getLocale('braveWalletTransactionCancel')} + + + {getLocale('braveWalletViewInActivity')} + + + + ) } diff --git a/components/brave_wallet_ui/constants/types.ts b/components/brave_wallet_ui/constants/types.ts index a3b4074ac517..0b99578659fa 100644 --- a/components/brave_wallet_ui/constants/types.ts +++ b/components/brave_wallet_ui/constants/types.ts @@ -1080,3 +1080,14 @@ export const SupportedSwapProviders = [ BraveWallet.SwapProvider.kZeroEx, BraveWallet.SwapProvider.kLiFi ] + +export type StorybookTransactionTypes = 'Send' | 'Swap' | 'Bridge' | 'Approve' +export const StorybookTransactionOptions: StorybookTransactionTypes[] = [ + 'Send', + 'Swap', + 'Bridge', + 'Approve' +] +export type StorybookTransactionArgs = { + transactionType: StorybookTransactionTypes +} diff --git a/components/brave_wallet_ui/panel/container.tsx b/components/brave_wallet_ui/panel/container.tsx index 90d5039de5d1..81dcb84c05c2 100644 --- a/components/brave_wallet_ui/panel/container.tsx +++ b/components/brave_wallet_ui/panel/container.tsx @@ -288,10 +288,13 @@ function Container() { if (selectedPanel === 'transactionStatus' && selectedTransactionId) { return ( - - + + - + ) } diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index bf9eed9484e7..1418d9bc3e81 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -1186,12 +1186,26 @@ provideStrings({ braveWalletAccountFilterAllAccounts: 'All accounts', // Transaction post-confirmation - + braveWalletGetHelp: 'Get help', + braveWalletTransactionTakingLongTime: 'Taking longer than expected?', + braveWalletViewInActivity: 'View in activity', + braveWalletSafelyDismissWindow: 'You can safely dismiss this window.', + braveWalletSendingAmountToAccount: 'Sending $1$5$2 to $3$6$4', + braveWalletAmountSentToAccount: '$1$5$2 has been sent to account $3$6$4', + braveWalletSwappingAmountToAmountOnNetwork: 'Swapping $1$5$2 to $3$6$4 on $7', + braveWalletAmountAddedToAccount: + 'The amount of $1$5$2 has been added to your account $3$6$4', + braveWalletBridgingAmountToNetwork: 'Bridging $1$5$2 to $3$6$4', + braveWalletUnableToSendSwapOrBridge: 'Unable to $1', + braveWalletErrorAttemptingToTransact: + 'There was an error attempting to $7 $1$5$2 to $3$6$4', + braveWalletApprovingAmountOnExchange: 'Approving $1$5$2 to $3$6$4', + braveWalletCancelTransactionDescription: + 'A new transaction will be created to cancel your existing transaction.', + braveWalletPendingTransactions: '$1 more transactions pending.', // Submitted braveWalletTransactionSubmittedTitle: 'Transaction submitted', - braveWalletTransactionSubmittedDescription: - 'Transaction has been successfully sent ' + - 'to the network and awaits confirmation.', + braveWalletTransactionSignedTitle: 'Transaction signed', // Failed braveWalletTransactionFailedHeaderTitle: '$1 was returned to your wallet', diff --git a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts index 163abe5d916b..efab049bceb9 100644 --- a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts +++ b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts @@ -4,7 +4,11 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. // Types -import { BraveWallet, SerializableTransactionInfo } from '../../constants/types' +import { + BraveWallet, + SerializableTransactionInfo, + StorybookTransactionTypes +} from '../../constants/types' import { deserializeTransaction } from '../../utils/model-serialization-utils' import { FileCoinTransactionInfo } from '../../utils/tx-utils' @@ -19,11 +23,13 @@ import { } from '../../common/constants/mocks' import { mockOriginInfo } from './mock-origin-info' import { mockEthAccount } from './mock-wallet-accounts' +import { mockBasicAttentionToken, mockUSDCoin } from './mock-asset-options' +import { LiFiExchangeProxy } from '../../common/constants/registry' export const mockTransactionInfo: SerializableTransactionInfo = { fromAccountId: mockAccount.accountId, fromAddress: mockAccount.address, - chainId: BraveWallet.SEPOLIA_CHAIN_ID, + chainId: BraveWallet.MAINNET_CHAIN_ID, id: '465a4d6646-kjlwf665', txArgs: ['0x0d8775f648430679a709e98d2b0cb6250d2887ef', '0x15ddf09c97b0000'], txDataUnion: { @@ -469,3 +475,63 @@ export const createMockTransactionInfo = (arg: { swapInfo } } + +const getMockTransactionType = ( + isSwapOrBridge: boolean, + transactionType: StorybookTransactionTypes +) => { + if (isSwapOrBridge) { + return BraveWallet.TransactionType.ETHSwap + } + if (transactionType === 'Approve') { + return BraveWallet.TransactionType.ERC20Approve + } + return BraveWallet.TransactionType.ETHSend +} + +export const getPostConfirmationStatusMockTransaction = ( + transactionType: StorybookTransactionTypes, + transactionStatus: BraveWallet.TransactionStatus +) => { + const isSwapOrBridge = + transactionType === 'Swap' || transactionType === 'Bridge' + + return { + ...mockTransactionInfo, + txArgs: [ + BraveWallet.TransactionType.ERC20Approve + ? LiFiExchangeProxy + : '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + '0x15ddf09c97b0000' + ], + txDataUnion: { + ...mockTransactionInfo.txDataUnion, + ethTxData1559: { + ...mockTransactionInfo.txDataUnion.ethTxData1559, + baseData: { + ...mockTransactionInfo.txDataUnion.ethTxData1559?.baseData, + to: mockBasicAttentionToken.contractAddress + } + } + }, + txStatus: transactionStatus, + txType: getMockTransactionType(isSwapOrBridge, transactionType), + swapInfo: isSwapOrBridge + ? ({ + fromCoin: BraveWallet.CoinType.ETH, + fromChainId: BraveWallet.MAINNET_CHAIN_ID, + fromAsset: mockBasicAttentionToken.contractAddress, + fromAmount: '9996544123665456564888', + toCoin: BraveWallet.CoinType.ETH, + toChainId: + transactionType === 'Bridge' + ? BraveWallet.SEPOLIA_CHAIN_ID + : BraveWallet.MAINNET_CHAIN_ID, + toAsset: mockUSDCoin.contractAddress, + toAmount: '111111111111111', + receiver: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + provider: 'lifi' + } as BraveWallet.SwapInfo) + : undefined + } as SerializableTransactionInfo +} diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index ca795c3636b4..9ddcda5fbf95 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -665,10 +665,27 @@ Show spam NFTs Portfolio settings All accounts + Get help + Taking longer than expected? + View in activity + You can safely dismiss this window. + Sending $120 ETH$5$2 to $3Ethereum Account 2$6$4 + $120 ETH$5$2 has been sent to account $3Ethereum Account 2$6$4 + Swapping $120 ETH$5$2 to $3Ethereum Account 2$6$4 on Ethereum Mainnet$7 + The amount of $120 ETH$5$2 has been added to your account $3Ethereum Account 2$6$4 + Bridging $120 ETH$5$2 to $3Polygon Mainnet$6$4 + Unable to Bridge$1 + There was an error attempting to Bridge$7 $120 ETH$5$2 to $3Polygon Mainnet$6$4 + Approving $120 ETH$5$2 to $3Li.Fi Excahnge$6$4 + A new transaction will be created to cancel your existing transaction. + + {PENDING_TRANSACTIONS, plural, + =1 {{PENDING_TRANSACTIONS} more transaction pending.} + other {{PENDING_TRANSACTIONS} more transactions pending.} + } + Transaction submitted - Transaction has been successfully sent to the network and awaits confirmation. Transaction signed - The transaction has been signed and will be sent to network by the DApp $11 ETH was returned to your wallet Transaction failed Transaction was failed due to a large price movement. Increase slippage tolerance to succeed at a larger price movement. diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn index b98c9cc1ac8f..b375d8ec6d14 100644 --- a/ui/webui/resources/BUILD.gn +++ b/ui/webui/resources/BUILD.gn @@ -346,6 +346,7 @@ leo_icons = [ "search-zoom-in.svg", "search.svg", "send.svg", + "send-filled.svg", "settings.svg", "share-macos.svg", "shield-done.svg", @@ -424,6 +425,7 @@ leo_icons = [ "sol-color.svg", "filecoin-color.svg", "slash.svg", + "xmark-color.svg", "yahoo-color.svg", "yandex-color.svg", ]