From a50ae55cb59820fe273018733161d5aae14b3318 Mon Sep 17 00:00:00 2001 From: Peter Sanderson Date: Thu, 23 Jan 2025 15:08:18 +0100 Subject: [PATCH] feat: UI facelift of the Affected transactions in the RBF flow --- .../components/src/components/Card/Card.tsx | 18 +- .../components/src/components/Card/types.tsx | 2 +- .../components/src/components/Card/utils.tsx | 7 +- .../AddressReplaceMeAfterAdamsMerge.tsx | 33 ++++ .../AffectedTransactionItem.tsx | 40 ++++ .../AffectedTransactions.tsx | 55 ++++++ .../AffectedTransactions/DecreasedOutputs.tsx | 186 ++++++++++++++++++ .../TxDetailModal/ChainedTxs.tsx | 2 +- .../ChangeFee/AffectedTransactionItem.tsx | 83 -------- .../ChangeFee/AffectedTransactions.tsx | 51 ----- .../TxDetailModal/ChangeFee/ChangeFee.tsx | 80 ++++---- .../ChangeFee/DecreasedOutputs.tsx | 171 ---------------- .../TxDetailModal/ChangeFee/GreyCard.tsx | 20 -- .../TxDetailModal/ChangeFee/WarnHeader.tsx | 37 ---- .../AdvancedTxDetails/AdvancedTxDetails.tsx | 2 +- .../TxDetailModal/TxDetailModalBase.tsx | 2 +- .../TransactionItem/TransactionItem.tsx | 5 +- packages/suite/src/hooks/wallet/useRbfForm.ts | 3 +- packages/suite/src/support/messages.ts | 22 ++- .../CoinmarketTransactionContainer.tsx | 2 +- .../views/wallet/send/TotalSent/TotalSent.tsx | 2 +- packages/urls/src/urls.ts | 4 +- packages/utils/src/index.ts | 1 - packages/utils/src/truncateMiddle.ts | 7 - 24 files changed, 407 insertions(+), 428 deletions(-) create mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AddressReplaceMeAfterAdamsMerge.tsx create mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactionItem.tsx create mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactions.tsx create mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/DecreasedOutputs.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactionItem.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactions.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/DecreasedOutputs.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/GreyCard.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/WarnHeader.tsx delete mode 100644 packages/utils/src/truncateMiddle.ts diff --git a/packages/components/src/components/Card/Card.tsx b/packages/components/src/components/Card/Card.tsx index df213573ae56..46b18623ac9a 100644 --- a/packages/components/src/components/Card/Card.tsx +++ b/packages/components/src/components/Card/Card.tsx @@ -4,7 +4,7 @@ import styled, { css } from 'styled-components'; import { borders, Elevation, spacingsPx } from '@trezor/theme'; -import { ElevationUp, useElevation } from '../ElevationContext/ElevationContext'; +import { ElevationContext, ElevationUp, useElevation } from '../ElevationContext/ElevationContext'; import { FrameProps, FramePropsKeys, @@ -38,7 +38,7 @@ const Container = styled.div<{ $fillType: FillType } & TransientProps - $fillType !== 'none' && theme.backgroundTertiaryDefaultOnElevation0}; + $fillType !== 'flat' && theme.backgroundTertiaryDefaultOnElevation0}; padding: ${spacingsPx.xxxs}; ${withFrameProps} @@ -65,11 +65,11 @@ const CardContainer = styled.div< position: relative; padding: ${mapPaddingTypeToPadding}; border-radius: ${borders.radii.md}; - transition: - background 0.3s, - box-shadow 0.2s, - border-color 0.2s; cursor: ${({ $isClickable }) => ($isClickable ? 'pointer' : 'default')}; + transition: + background 0.5s, + border 0.5s, + box-shadow 0.5s; ${({ theme, $variant, $paddingType }) => $variant && @@ -145,7 +145,11 @@ const CardComponent = forwardRef( {...rest} data-testid={dataTest} > - {fillType === 'none' ? children : {children}} + {fillType === 'flat' ? ( + {children} + ) : ( + {children} + )} ); }, diff --git a/packages/components/src/components/Card/types.tsx b/packages/components/src/components/Card/types.tsx index 03dc3457f884..d5aa8d6961c3 100644 --- a/packages/components/src/components/Card/types.tsx +++ b/packages/components/src/components/Card/types.tsx @@ -3,7 +3,7 @@ import { UIVariant } from '../../config/types'; export const paddingTypes = ['small', 'none', 'normal', 'large'] as const; export type PaddingType = (typeof paddingTypes)[number]; -export const fillTypes = ['none', 'default'] as const; +export const fillTypes = ['flat', 'default'] as const; export type FillType = (typeof fillTypes)[number]; export const cardVariants = ['primary', 'warning'] as const; diff --git a/packages/components/src/components/Card/utils.tsx b/packages/components/src/components/Card/utils.tsx index a0af11a8ff6c..b728633d5307 100644 --- a/packages/components/src/components/Card/utils.tsx +++ b/packages/components/src/components/Card/utils.tsx @@ -4,7 +4,6 @@ import { spacingsPx, Elevation, mapElevationToBackground, - mapElevationToBorder, SpacingPxValues, CSSColor, } from '@trezor/theme'; @@ -61,6 +60,7 @@ export const mapFillTypeToCSS = ({ default: css` background: ${mapElevationToBackground({ $elevation, theme })}; box-shadow: ${$elevation === 1 && !$hasLabel && theme.boxShadowBase}; + border: 1px solid transparent; ${$isClickable && css` @@ -69,8 +69,9 @@ export const mapFillTypeToCSS = ({ } `} `, - none: css` - border: 1px solid ${mapElevationToBorder({ $elevation, theme })}; + flat: css` + background: ${theme.backgroundSurfaceElevationNegative}; + border: 1px solid ${theme.borderElevation0}; `, }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AddressReplaceMeAfterAdamsMerge.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AddressReplaceMeAfterAdamsMerge.tsx new file mode 100644 index 000000000000..684f16e2b5c0 --- /dev/null +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AddressReplaceMeAfterAdamsMerge.tsx @@ -0,0 +1,33 @@ +import styled from 'styled-components'; + +// Todo: replace once Adams PR is merged (here https://github.com/trezor/trezor-suite/pull/16465 but shall be separated ane generalized first) +const AddressWrapper = styled.p` + text-wrap: balance; + font-variant-numeric: tabular-nums; + letter-spacing: 0; +`; + +// Todo: replace once Adams PR is merged (here https://github.com/trezor/trezor-suite/pull/16465 but shall be separated ane generalized first) +const addSpacing = (value: string) => value.match(/.{1,4}/g)?.join(' ') || value; + +// Todo: replace once Adams PR is merged (here https://github.com/trezor/trezor-suite/pull/16465 but shall be separated ane generalized first) +export type AddressProps = { + value: string; + isTruncated?: boolean; +}; + +// Todo: replace once Adams PR is merged (here https://github.com/trezor/trezor-suite/pull/16465 but shall be separated ane generalized first) +export const AddressReplaceMeAfterAdamsMerge = ({ value, isTruncated }: AddressProps) => { + const formattedValue = isTruncated + ? `${addSpacing(value.slice(0, 8))} ... ${addSpacing(value.slice(-8))}` + : addSpacing(value); + + const handleCopy = (e: React.ClipboardEvent) => { + const selection = window.getSelection()?.toString(); + + e.preventDefault(); + e.clipboardData?.setData('text/plain', selection?.replace(/\s/g, '') ?? value); + }; + + return {formattedValue}; +}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactionItem.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactionItem.tsx new file mode 100644 index 000000000000..cd505dd9851f --- /dev/null +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactionItem.tsx @@ -0,0 +1,40 @@ +import { Icon, InfoSegments, Row, Text } from '@trezor/components'; +import { WalletAccountTransaction } from '@suite-common/wallet-types'; +import { Transaction } from '@trezor/blockchain-link-types'; +import { spacings } from '@trezor/theme'; + +import { FormattedDate, HiddenPlaceholder } from 'src/components/suite'; + +import { AddressReplaceMeAfterAdamsMerge } from './AddressReplaceMeAfterAdamsMerge'; + +type RowIcon = { + txType: Transaction['type']; + isAccountOwned: boolean | undefined; +}; + +const RowIcon = ({ txType, isAccountOwned }: RowIcon) => { + const iconType = txType === 'recv' ? 'receive' : 'send'; + + return ; +}; + +type AffectedTransactionItemProps = { + tx: WalletAccountTransaction; + isAccountOwned?: boolean; +}; + +export const AffectedTransactionItem = ({ tx, isAccountOwned }: AffectedTransactionItemProps) => ( + + + + + {tx.blockTime && } + + + + + + + + +); diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactions.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactions.tsx new file mode 100644 index 000000000000..d459183644eb --- /dev/null +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/AffectedTransactions.tsx @@ -0,0 +1,55 @@ +import { Banner, Card, Column, Divider, Link, Row, Table, Text } from '@trezor/components'; +import { spacings } from '@trezor/theme'; +import { ChainedTransactions } from '@suite-common/wallet-types'; + +import { Translation } from 'src/components/suite'; + +import { AffectedTransactionItem } from './AffectedTransactionItem'; + +type AffectedTransactionsProps = { + chainedTxs?: ChainedTransactions; + showChained: () => void; +}; + +export const AffectedTransactions = ({ chainedTxs, showChained }: AffectedTransactionsProps) => { + if (chainedTxs === undefined) { + return null; + } + + return ( + + + + + + + + + + + + + + + + + + {chainedTxs.own.map(tx => ( + + + + + + ))} + {chainedTxs.others.map(tx => ( + + + + + + ))} +
+
+
+ ); +}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/DecreasedOutputs.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/DecreasedOutputs.tsx new file mode 100644 index 000000000000..349da1f22d5b --- /dev/null +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/AffectedTransactions/DecreasedOutputs.tsx @@ -0,0 +1,186 @@ +import styled from 'styled-components'; + +import { Icon, Banner, Column, Card, Row, Divider, Text, Link } from '@trezor/components'; +import { formatNetworkAmount } from '@suite-common/wallet-utils'; +import { spacings } from '@trezor/theme'; +import { NetworkSymbol } from '@suite-common/wallet-config'; +import { HELP_CENTER_REPLACE_BY_FEE_BITCOIN } from '@trezor/urls'; +import { Account, FormState } from '@suite-common/wallet-types'; + +import { FormattedCryptoAmount, HiddenPlaceholder } from 'src/components/suite'; +import { Translation, TranslationKey } from 'src/components/suite/Translation'; +import { RbfContextValues, useRbfContext } from 'src/hooks/wallet/useRbfForm'; + +import { AddressReplaceMeAfterAdamsMerge } from './AddressReplaceMeAfterAdamsMerge'; + +// Todo: reolace with Albinas component once its merged +// eslint-disable-next-line local-rules/no-override-ds-component +const SelectableCard = styled(Card)<{ $isSelected: boolean }>` + ${({ $isSelected, theme }) => + $isSelected ? `border: 2px solid ${theme.borderSecondary}` : undefined} +`; + +type AmountRowProps = { + labelTranslationKey: TranslationKey; + shouldSendInSats: boolean | undefined; + amount: string; + symbol: NetworkSymbol; +}; + +const AmountItem = ({ labelTranslationKey, shouldSendInSats, amount, symbol }: AmountRowProps) => { + const value = shouldSendInSats ? formatNetworkAmount(amount, symbol) : amount; + + return ( + + + + + + + ); +}; + +type ReducedAmount = { + composedLevels: RbfContextValues['composedLevels']; + setMaxOutputId: number; + account: Account; + selectedFee: FormState['selectedFee']; +}; + +const ReducedAmount = ({ composedLevels, setMaxOutputId, account, selectedFee }: ReducedAmount) => { + if (!composedLevels) { + return null; + } + + const precomposedTx = composedLevels[selectedFee || 'normal']; + + if (precomposedTx.type !== 'final') { + return null; + } + + return ( + <> + + + + ); +}; + +export const DecreasedOutputs = () => { + const { + showDecreasedOutputs, + formValues, + account, + coinjoinRegisteredUtxos, + getValues, + setValue, + composedLevels, + composeRequest, + shouldSendInSats, + } = useRbfContext(); + const { selectedFee, setMaxOutputId } = getValues(); + + // no set-max means that no output was decreased + if (!showDecreasedOutputs || typeof setMaxOutputId !== 'number') return null; + + // find all outputs possible to reduce + const useRadio = formValues.outputs.filter(o => typeof o.address === 'string').length > 1; + + const getDecreaseWarring = (): TranslationKey => { + if (account.accountType === 'coinjoin') { + if (coinjoinRegisteredUtxos.length > 0) { + return 'TR_UTXO_REGISTERED_IN_COINJOIN_RBF_WARNING'; + } else { + return 'TR_NOT_ENOUGH_ANONYMIZED_FUNDS_RBF_WARNING'; + } + } + + return 'TR_DECREASE_TX'; + }; + + return ( + + + + + + + + + + + + + + + + + + {useRadio && ( + + + + )} + + {formValues.outputs.flatMap((output, i) => { + if (typeof output.address !== 'string') return null; + const isChecked = setMaxOutputId === i; + + return ( + // it's safe to use array index as key since outputs do not change + { + setValue('setMaxOutputId', i); + composeRequest(); + } + : undefined + } + $isSelected={useRadio && isChecked} + > + + + {isChecked && ( + + )} + + + + + + + + + + + ); + })} + + + + ); +}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChainedTxs.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChainedTxs.tsx index ce866c504841..d83450d199ba 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChainedTxs.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChainedTxs.tsx @@ -7,7 +7,7 @@ import { AccountType, Network } from '@suite-common/wallet-config'; import { TrezorLink, Translation } from 'src/components/suite'; import { TransactionItem } from 'src/components/wallet/TransactionItem/TransactionItem'; -import { AffectedTransactionItem } from './ChangeFee/AffectedTransactionItem'; +import { AffectedTransactionItem } from './AffectedTransactions/AffectedTransactionItem'; const Wrapper = styled.div` text-align: left; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactionItem.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactionItem.tsx deleted file mode 100644 index 2f84d55b0408..000000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactionItem.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import styled, { useTheme } from 'styled-components'; - -import { Icon, variables } from '@trezor/components'; -import { WalletAccountTransaction } from '@suite-common/wallet-types'; -import { truncateMiddle } from '@trezor/utils'; - -import { FormattedDate } from 'src/components/suite'; -import { useLayoutSize } from 'src/hooks/suite/useLayoutSize'; - -const TxRow = styled.div` - display: flex; - align-items: center; - padding: 4px 0; -`; - -const IconWrapper = styled.div` - padding-right: 24px; -`; - -const Text = styled.span` - color: ${({ theme }) => theme.legacy.TYPE_LIGHT_GREY}; - font-weight: ${variables.FONT_WEIGHT.MEDIUM}; - font-size: ${variables.FONT_SIZE.SMALL}; - font-variant-numeric: tabular-nums; -`; - -const Txid = styled(Text)` - text-overflow: ellipsis; - overflow: hidden; - flex: 1; - font-variant-numeric: slashed-zero tabular-nums; -`; - -const Timestamp = styled(Text)` - white-space: nowrap; - margin-left: 4px; -`; - -const Bullet = styled.div` - margin-left: 8px; - margin-right: 8px; - color: ${({ theme }) => theme.legacy.TYPE_LIGHT_GREY}; -`; - -export const AffectedTransactionItem = ({ - tx, - isAccountOwned, - className, -}: { - tx: WalletAccountTransaction; - isAccountOwned?: boolean; - className?: string; -}) => { - const theme = useTheme(); - const { isMobileLayout } = useLayoutSize(); - const shownTxidChars = isMobileLayout ? 4 : 8; - const iconType = tx.type === 'recv' ? 'receive' : 'send'; - - return ( - - {!isMobileLayout && ( - - - - )} - - {tx.blockTime && ( - <> - - - - - - )} - - {truncateMiddle(tx.txid, shownTxidChars, shownTxidChars + 2)} - - ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactions.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactions.tsx deleted file mode 100644 index 7b1bcb8bf00b..000000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/AffectedTransactions.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import styled from 'styled-components'; - -import { Button } from '@trezor/components'; - -import { Translation } from 'src/components/suite'; -import { useRbfContext } from 'src/hooks/wallet/useRbfForm'; - -import { GreyCard } from './GreyCard'; -import { WarnHeader } from './WarnHeader'; -import { AffectedTransactionItem } from './AffectedTransactionItem'; - -const ChainedTxs = styled.div` - display: flex; - flex-direction: column; - padding-top: 24px; - margin-top: 24px; - border-top: 1px solid ${({ theme }) => theme.legacy.STROKE_GREY}; -`; - -export const AffectedTransactions = ({ showChained }: { showChained: () => void }) => { - const { chainedTxs } = useRbfContext(); - - if (!chainedTxs) return null; - - return ( - - - - - } - > - - - - {chainedTxs.own.map(tx => ( - - ))} - {chainedTxs.others.map(tx => ( - - ))} - - - ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/ChangeFee.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/ChangeFee.tsx index 53b9bcd41d9c..e73501d785ff 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/ChangeFee.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/ChangeFee.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; -import { Divider, Card, InfoItem, Row, Text } from '@trezor/components'; +import { Divider, Card, InfoItem, Row, Text, Column } from '@trezor/components'; import { formatNetworkAmount, getFeeUnits } from '@suite-common/wallet-utils'; import { WalletAccountTransaction } from '@suite-common/wallet-types'; import { spacings } from '@trezor/theme'; @@ -10,8 +10,8 @@ import { useSelector } from 'src/hooks/suite'; import { useRbfContext, UseRbfProps } from 'src/hooks/wallet/useRbfForm'; import { RbfFees } from './RbfFees'; -import { AffectedTransactions } from './AffectedTransactions'; -import { DecreasedOutputs } from './DecreasedOutputs'; +import { AffectedTransactions } from '../AffectedTransactions/AffectedTransactions'; +import { DecreasedOutputs } from '../AffectedTransactions/DecreasedOutputs'; /* children are only for test purposes, this prop is not available in regular build */ interface ChangeFeeProps extends UseRbfProps { @@ -24,6 +24,7 @@ const ChangeFeeLoaded = (props: ChangeFeeProps) => { const { tx, showChained, children } = props; const { account: { networkType }, + chainedTxs, } = useRbfContext(); const feeRate = @@ -31,43 +32,52 @@ const ChangeFeeLoaded = (props: ChangeFeeProps) => { const fee = formatNetworkAmount(tx.fee, tx.symbol); return ( - - - - {feeRate && ` (${feeRate})`} - - } - typographyStyle="body" - > - - - - - - - + <> + + + + + + + + + {feeRate && ` (${feeRate})`} + + } + typographyStyle="body" + > + + + + + + + - + - + + + {children} + + - - {children} - + + ); }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/DecreasedOutputs.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/DecreasedOutputs.tsx deleted file mode 100644 index f67534611ac6..000000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/DecreasedOutputs.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { ReactNode } from 'react'; - -import styled from 'styled-components'; -import { AnimatePresence, motion } from 'framer-motion'; - -import { Icon, variables, Radio, motionAnimation } from '@trezor/components'; -import { formatNetworkAmount } from '@suite-common/wallet-utils'; -import { spacings } from '@trezor/theme'; - -import { FormattedCryptoAmount, HiddenPlaceholder } from 'src/components/suite'; -import { Translation, TranslationKey } from 'src/components/suite/Translation'; -import { useRbfContext } from 'src/hooks/wallet/useRbfForm'; - -import { GreyCard } from './GreyCard'; -import { WarnHeader } from './WarnHeader'; - -const OutputsWrapper = styled.div` - display: flex; - flex-direction: column; - padding-top: 12px; - margin-top: 24px; - border-top: 1px solid ${({ theme }) => theme.legacy.STROKE_GREY}; -`; - -const Output = styled.div` - display: flex; - align-items: center; - padding: 8px 0; -`; - -const OutputInner = styled.div` - display: flex; - flex-direction: column; -`; - -const OutputLabel = styled.div<{ $isChecked: boolean }>` - display: flex; - align-items: center; - font-size: ${variables.FONT_SIZE.NORMAL}; - line-height: 24px; /* icon height */ - font-weight: ${$props => - $props.$isChecked ? variables.FONT_WEIGHT.DEMI_BOLD : variables.FONT_WEIGHT.MEDIUM}; - color: ${({ $isChecked, theme }) => ($isChecked ? theme.legacy.TYPE_GREEN : 'inherit')}; -`; - -const OutputAddress = styled.div<{ $isChecked: boolean }>` - font-size: ${variables.FONT_SIZE.TINY}; - font-weight: ${$props => - $props.$isChecked ? variables.FONT_WEIGHT.DEMI_BOLD : variables.FONT_WEIGHT.MEDIUM}; - color: ${({ $isChecked, theme }) => ($isChecked ? theme.legacy.TYPE_DARK_GREY : 'inherit')}; - padding-top: 2px; -`; - -const ReducedAmount = styled.span` - display: flex; - align-items: center; -`; - -export const DecreasedOutputs = () => { - const { - showDecreasedOutputs, - formValues, - account, - coinjoinRegisteredUtxos, - getValues, - setValue, - composedLevels, - composeRequest, - shouldSendInSats, - } = useRbfContext(); - const { selectedFee, setMaxOutputId } = getValues(); - - // no set-max means that no output was decreased - if (!showDecreasedOutputs || typeof setMaxOutputId !== 'number') return null; - - let reducedAmount: ReactNode = null; - if (composedLevels) { - const precomposedTx = composedLevels[selectedFee || 'normal']; - if (precomposedTx.type === 'final') { - reducedAmount = ( - - - - - ); - } - } - - // find all outputs possible to reduce - const useRadio = formValues.outputs.filter(o => typeof o.address === 'string').length > 1; - - let decreaseWarning: TranslationKey = 'TR_DECREASE_TX'; - if (account.accountType === 'coinjoin') { - if (coinjoinRegisteredUtxos.length > 0) { - decreaseWarning = 'TR_UTXO_REGISTERED_IN_COINJOIN_RBF_WARNING'; - } else { - decreaseWarning = 'TR_NOT_ENOUGH_ANONYMIZED_FUNDS_RBF_WARNING'; - } - } - - return ( - - - - - - - - {formValues.outputs.flatMap((o, i) => { - if (typeof o.address !== 'string') return null; - const isChecked = setMaxOutputId === i; - - return ( - // it's safe to use array index as key since outputs do not change - - - {useRadio && ( - { - setValue('setMaxOutputId', i); - composeRequest(); - }} - isChecked={isChecked} - margin={{ right: spacings.xs }} - /> - )} - - - - ), - }} - /> - {isChecked && reducedAmount} - - - {o.address} - - - - ); - })} - - - - - ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/GreyCard.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/GreyCard.tsx deleted file mode 100644 index 066747b21982..000000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/GreyCard.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { ReactNode } from 'react'; - -import styled from 'styled-components'; - -import { Card, Column } from '@trezor/components'; - -// eslint-disable-next-line local-rules/no-override-ds-component -const Wrapper = styled(Card)` - text-align: left; - background-color: ${({ theme }) => theme.legacy.BG_GREY}; - margin-left: -10px; - margin-right: -10px; - margin-top: 12px; -`; - -export const GreyCard = (props: { children?: ReactNode }) => ( - - {props.children} - -); diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/WarnHeader.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/WarnHeader.tsx deleted file mode 100644 index 4effaf18ad0c..000000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/ChangeFee/WarnHeader.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { ReactNode, HTMLAttributes } from 'react'; - -import styled, { useTheme } from 'styled-components'; - -import { Icon, variables } from '@trezor/components'; - -const Header = styled.div` - display: flex; - margin-top: 4px; - color: ${({ theme }) => theme.legacy.TYPE_ORANGE}; - font-weight: ${variables.FONT_WEIGHT.DEMI_BOLD}; - font-size: ${variables.FONT_SIZE.TINY}; - align-items: center; -`; - -const Body = styled.div` - display: flex; - flex: 1; - padding: 0 16px; -`; - -interface WarnHeaderProps extends HTMLAttributes { - action?: ReactNode; - children?: ReactNode; -} - -export const WarnHeader = ({ action, children, ...rest }: WarnHeaderProps) => { - const theme = useTheme(); - - return ( -
- - {children} - {action} -
- ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/Detail/AdvancedTxDetails/AdvancedTxDetails.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/Detail/AdvancedTxDetails/AdvancedTxDetails.tsx index 7c23ac5e74c2..d1cdb40ab2c1 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/Detail/AdvancedTxDetails/AdvancedTxDetails.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/Detail/AdvancedTxDetails/AdvancedTxDetails.tsx @@ -61,7 +61,7 @@ export const AdvancedTxDetails = ({ }; return ( - + setSelectedTab('amount')}> diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/TxDetailModalBase.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/TxDetailModalBase.tsx index 1466d3a1c68b..9e9ef6b8eb79 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/TxDetailModalBase.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/TxDetailModal/TxDetailModalBase.tsx @@ -55,7 +55,7 @@ export const TxDetailModalBase = ({ bottomContent={bottomContent} onBackClick={onBackClick} > - + ( {disableBumpFee ? ( diff --git a/packages/suite/src/hooks/wallet/useRbfForm.ts b/packages/suite/src/hooks/wallet/useRbfForm.ts index 003e84ebccdd..a26c3cd9a766 100644 --- a/packages/suite/src/hooks/wallet/useRbfForm.ts +++ b/packages/suite/src/hooks/wallet/useRbfForm.ts @@ -285,7 +285,8 @@ export const useRbf = (props: UseRbfProps) => { }; // context accepts only valid state (non-nullable account) -type RbfContextValues = ReturnType & NonNullable>; +export type RbfContextValues = ReturnType & + NonNullable>; export const RbfContext = createContext(null); RbfContext.displayName = 'RbfContext'; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 101a86d82c07..edfe08c6e471 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -6127,6 +6127,10 @@ export default defineMessages({ id: 'TR_CHAINED_TXS', defaultMessage: 'Chained transactions', }, + TR_AMOUNT_REDUCED_TXS: { + id: 'TR_AMOUNT_REDUCED_TXS', + defaultMessage: 'Amount reduced', + }, TR_DATA: { id: 'TR_DATA', defaultMessage: 'Data', @@ -6379,6 +6383,10 @@ export default defineMessages({ id: 'TR_BUMP_FEE', defaultMessage: 'Bump fee', }, + TR_BUMP_FEE_SUBTEXT: { + id: 'TR_BUMP_FEE_SUBTEXT', + defaultMessage: 'Speed up this transaction confirmation by paying a higher fee. ', + }, TR_REPLACE_TX: { id: 'TR_REPLACE_TX', defaultMessage: 'Replace transaction', @@ -6407,9 +6415,17 @@ export default defineMessages({ id: 'TR_DECREASE_TX', defaultMessage: 'No funds left for fee. Final amount needs to be reduced to bump fee.', }, - TR_REDUCE_FROM: { - id: 'TR_REDUCE_FROM', - defaultMessage: 'Reduce from {value}', + TR_RBF_ORIGINAL_AMOUNT: { + id: 'TR_RBF_ORIGINAL_AMOUNT', + defaultMessage: 'Original amount', + }, + TR_RBF_NEW_AMOUNT: { + id: 'TR_RBF_NEW_AMOUNT', + defaultMessage: 'New amount', + }, + TR_DECREASED_AMOUNT_SELECTION_EXPLANATION: { + id: 'TR_DECREASED_AMOUNT_SELECTION_EXPLANATION', + defaultMessage: 'Select the amount that should be reduced to pay for the increased fee.', }, TR_DECREASE_AMOUNT_BY: { id: 'TR_DECREASE_AMOUNT_BY', diff --git a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketTransactions/CoinmarketTransaction/CoinmarketTransactionContainer.tsx b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketTransactions/CoinmarketTransaction/CoinmarketTransactionContainer.tsx index c3ccb8b769a0..4656cc2301a3 100644 --- a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketTransactions/CoinmarketTransaction/CoinmarketTransactionContainer.tsx +++ b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketTransactions/CoinmarketTransaction/CoinmarketTransactionContainer.tsx @@ -16,7 +16,7 @@ export const CoinmarketTransactionContainer = ({ const isMobile = useMediaQuery(`(max-width: ${variables.SCREEN_SIZE.SM})`); return ( - + {TradeDetail} diff --git a/packages/suite/src/views/wallet/send/TotalSent/TotalSent.tsx b/packages/suite/src/views/wallet/send/TotalSent/TotalSent.tsx index 9acb7ad8218d..65f94790857a 100644 --- a/packages/suite/src/views/wallet/send/TotalSent/TotalSent.tsx +++ b/packages/suite/src/views/wallet/send/TotalSent/TotalSent.tsx @@ -29,7 +29,7 @@ export const TotalSent = () => { return ( - + } diff --git a/packages/urls/src/urls.ts b/packages/urls/src/urls.ts index 895ebb1dad8b..d46ee4e47f72 100644 --- a/packages/urls/src/urls.ts +++ b/packages/urls/src/urls.ts @@ -113,8 +113,10 @@ export const HELP_CENTER_EVM_SEND_TO_CONTRACT_URL = 'https://trezor.io/support/a/where-is-my-ethereum'; export const HELP_CENTER_FIRMWARE_REVISION_CHECK: Url = 'https://trezor.io/learn/a/trezor-firmware-revision-check'; -export const HELP_CENTER_REPLACE_BY_FEE: Url = +export const HELP_CENTER_REPLACE_BY_FEE_ETHEREUM: Url = 'https://trezor.io/learn/a/replace-by-fee-rbf-ethereum'; +export const HELP_CENTER_REPLACE_BY_FEE_BITCOIN = + 'https://trezor.io/learn/a/replace-by-fee-rbf-bitcoin'; export const INVITY_URL: Url = 'https://invity.io/'; export const INVITY_SCHEDULE_OF_FEES: Url = 'https://blog.invity.io/schedule-of-fees'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 3a6a4e035ed8..281b0d41292d 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -50,7 +50,6 @@ export * from './splitStringEveryNCharacters'; export * from './throttler'; export * from './throwError'; export * from './topologicalSort'; -export * from './truncateMiddle'; export * from './typedEventEmitter'; export * from './urlToOnion'; export * from './zip'; diff --git a/packages/utils/src/truncateMiddle.ts b/packages/utils/src/truncateMiddle.ts deleted file mode 100644 index de19c2a4564e..000000000000 --- a/packages/utils/src/truncateMiddle.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const truncateMiddle = (text: string, startChars: number, endChars: number) => { - if (text.length <= startChars + endChars) return text; - const start = text.substring(0, startChars); - const end = text.substring(text.length - endChars, text.length); - - return `${start}…${end}`; -};