Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- iOS: Add haptic feedback on toggle component
- Apply rounded corners in several places and components of the app
- Send: enable rotating currencies in send window.
- Show the fee's fiat value in send-to-self txs for consistency with others, and include the send-to-self amount in the left-side label

## v4.50.1
- Fix a bug that would delay showing watch-only accounts.
Expand Down
25 changes: 9 additions & 16 deletions frontends/web/src/components/amount/conversion-amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { useContext } from 'react';
import type { TAmountWithConversions, TTransactionType } from '@/api/account';
import { RatesContext } from '@/contexts/RatesContext';
import { Arrow } from '@/components/transactions/components/arrows';
import { Amount } from '@/components/amount/amount';
import { getTxSign } from '@/utils/transaction';
import styles from './conversion-amount.module.css';
Expand All @@ -17,41 +16,35 @@ type TConversionAmountProps = {
const btcUnits: Readonly<string[]> = ['BTC', 'TBTC', 'sat', 'tsat'];

/**
* Renders a formattted conversion amount optionally with send-to-self icon or estimate symbol
* Renders a formatted conversion amount, optionally with an estimate symbol.
*/
export const ConversionAmount = ({
amount,
deductedAmount,
type,
}: TConversionAmountProps) => {
const { defaultCurrency } = useContext(RatesContext);
const conversion = amount?.conversions && amount?.conversions[defaultCurrency];

const sign = getTxSign(type);
const estimatedPrefix = '\u2248'; // ≈
const sendToSelf = type === 'send_to_self';
const recv = type === 'receive';
const amountToShow = recv || sendToSelf ? amount : deductedAmount;
const conversionUnit = sendToSelf ? amountToShow.unit : defaultCurrency;
const amountToShow = recv ? amount : deductedAmount;
const conversionUnit = defaultCurrency;
const conversion = amountToShow?.conversions && amountToShow?.conversions[defaultCurrency];

// we skip the estimated conversion prefix when the Tx is send to self, or both coin and conversion are in BTC units.
const skipEstimatedPrefix = sendToSelf || (btcUnits.includes(conversionUnit) && btcUnits.includes(amountToShow.unit));
// we skip the estimated conversion prefix when both coin and conversion are in BTC units.
const skipEstimatedPrefix = btcUnits.includes(conversionUnit) && btcUnits.includes(amountToShow.unit);

return (
<span className={styles.txConversionAmount}>
{(conversion || sendToSelf) && amountToShow ? (
{conversion && amountToShow ? (
<>
{sendToSelf && (
<span className={styles.txSmallInlineIcon}>
<Arrow type="send_to_self" />
</span>
)}
{amountToShow.estimated && !skipEstimatedPrefix && (
<span className={styles.txPrefix}>{estimatedPrefix}{' '}</span>
)}
{conversion && conversion !== '0' && !sendToSelf ? sign : null}
{conversion !== '0' ? sign : null}
<Amount
amount={sendToSelf ? amountToShow.amount : conversion || ''}
amount={conversion || ''}
unit={conversionUnit}
/>
<span className={styles.txUnit}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@
color: var(--color-default);
font-size: var(--size-default);
line-height: 1.25;
max-width: 100%;
text-overflow: ellipsis;
}

.txDateLong {
Expand Down Expand Up @@ -228,8 +230,7 @@
}

.addresses {
opacity: 0;
pointer-events: none;
display: none;
}

.txNoteWithAddress {
Expand All @@ -239,4 +240,4 @@
.iconLoupe {
width: 28px;
vertical-align: text-bottom;
}
}
69 changes: 56 additions & 13 deletions frontends/web/src/components/transactions/transaction.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

import { useTranslation } from 'react-i18next';
import { Trans, useTranslation } from 'react-i18next';
import type { TAmountWithConversions, TTransactionStatus, TTransactionType, TTransaction } from '@/api/account';
import { useMediaQuery } from '@/hooks/mediaquery';
import { Loupe } from '@/components/icon/icon';
Expand Down Expand Up @@ -44,6 +44,7 @@ export const Transaction = ({
</span>
<Status
addresses={addresses}
amount={amountAtTime}
note={note}
numConfirmations={numConfirmations}
numConfirmationsComplete={numConfirmationsComplete}
Expand All @@ -70,6 +71,7 @@ export const Transaction = ({

type TStatus = {
addresses: string[];
amount: TAmountWithConversions;
note?: TTransaction['note'];
numConfirmations: number;
numConfirmationsComplete: number;
Expand All @@ -80,6 +82,7 @@ type TStatus = {

const Status = ({
addresses,
amount,
note,
numConfirmations,
numConfirmationsComplete,
Expand All @@ -102,6 +105,7 @@ const Status = ({
) : (
<Addresses
addresses={addresses}
amount={amount}
status={status}
type={type}
/>
Expand Down Expand Up @@ -151,10 +155,13 @@ const Amounts = ({
const displayAmount = recv ? amount : deductedAmount;

return (
<span className={`
<span
className={`
${styles.txAmountsColumn || ''}
${styles[txTypeClass] || ''}
`}>
`}
data-testid="tx-amounts"
>
<span className={styles.txAmount}>
{displayAmount.amount !== '0' && getTxSign(type)}
<AmountWithUnit
Expand All @@ -171,6 +178,22 @@ type TDateProps = {
time: string | null;
};

type TAddressListProps = {
values: string[];
};

const AddressList = ({ values }: TAddressListProps) => (
<span className={styles.addresses}>
{values[0]}
{values.length > 1 && (
<span>
{' '}
(+{values.length - 1})
</span>
)}
</span>
);

const Date = ({
time,
}: TDateProps) => {
Expand All @@ -192,39 +215,59 @@ const Date = ({

type TAddresses = {
addresses: TTransaction['addresses'];
amount: TAmountWithConversions;
status: TTransactionStatus;
type: TTransactionType;
};

const Addresses = ({
addresses,
amount,
status,
type,
}: TAddresses) => {
const { t } = useTranslation();
const isMobile = useMediaQuery('(max-width: 768px)');

if (type === 'send_to_self') {
const labelKey = status === 'failed'
? 'transaction.tx.send_to_self_failed'
: 'transaction.tx.send_to_self';
return (
<span className={styles.txNoteWithAddress}>
<span className={styles.txType}>
<Trans
i18nKey={labelKey}
components={{
amount: (
<AmountWithUnit
amount={amount}
unitClassName={styles.txUnit}
/>
),
}}
/>
</span>
{' '}
<AddressList values={addresses} />
</span>
);
}

const label = isMobile
? (type === 'receive' ? t('generic.received') : t('generic.sent'))
: (type === 'receive'
? t('transaction.tx.receive', { context: status })
: t('transaction.tx.send', { context: status })
// send_to_self will currently show the send message
);

return (
<span className={styles.txNoteWithAddress}>
<span className={styles.txType}>
{label}
</span>
<span className={styles.addresses}>
{addresses[0]}
{addresses.length > 1 && (
<span>
{' '}
(+{addresses.length - 1})
</span>
)}
</span>
{' '}
<AddressList values={addresses} />
</span>
);
};
4 changes: 3 additions & 1 deletion frontends/web/src/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -1961,7 +1961,9 @@
"receive_pending": "Received to",
"send_complete": "Sent to",
"send_failed": "Failed sending to",
"send_pending": "Sent to"
"send_pending": "Sent to",
"send_to_self": "Sent <amount /> to self",
"send_to_self_failed": "Failed sending <amount /> to self"
},
"vsize": "Virtual size",
"weight": "Weight"
Expand Down
7 changes: 4 additions & 3 deletions frontends/web/tests/send.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,12 @@ test('Send BTC', async ({ page, host, frontendPort, servewalletPort }, testInfo)
await page.getByTestId('close-button').click();

// Verify that the values displayed are correctly
const shownDetractedAmount = await newTx.getByTestId('amountBlocks').nth(0).textContent();
const shownSentToSelfAmount = await newTx.getByTestId('amountBlocks').nth(1).textContent();
const labelAmount = await newTx.getByTestId('amountBlocks').first().textContent();
const amountsColumn = newTx.getByTestId('tx-amounts');
const shownDetractedAmount = await amountsColumn.getByTestId('amountBlocks').nth(0).textContent();

expect(labelAmount).toBe(amount);
expect(shownDetractedAmount).toBe(fee);
expect(shownSentToSelfAmount).toBe(amount);

});

Expand Down
Loading