Repository: BitBoxSwiss/bitbox-wallet-app
Pull request #3219: frontend/balance: add BTC/sat rotating unit in account balance
Author: login: Beerosagos
Created: 2025-03-05T08:40:13Z | Merged: 2025-03-25T11:40:43Z

Previously it was possible to switch between BTC and sat unit using a switch component in the general settings. This change allows the user to switch unit more easily without leaving the account view.

This also heavily refactors the FiatConversion component, which is now renamed AmountWithUnit.

frontend/web/src/components/rates/rates.* have been moved and renamed to frontend/web/src/components/amount/amount-with-unit.*

Note: while our currency rotational unit is based on the fact that all the fiat conversions are fetched inside the amounts, this doesn't happen for btc/sat switching. This means that I had to "observe" the unit change in the account page to update the balance and the transaction list, i.e. it is an async change. It seems good enough to me (and I really don't want to over complicate things adding BTC/sat conversion in the amount), but let me know what you think about this.


Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed9a11e6cb..22a9038a40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 # Changelog
 
 ## Unreleased
+- Removed the BTC/sat switch from the general settings in favor of a rotating unit in the account balance.
 - Reduced support for BitBox01
 - Fix a bug that would prevent the app to perform firmware upgrade when offline.
 - Replace sidebar with bottom navigation bar for mobile devices
diff --git a/backend/handlers/handlers.go b/backend/handlers/handlers.go
index 1d7cbbb820..6c121916cb 100644
--- a/backend/handlers/handlers.go
+++ b/backend/handlers/handlers.go
@@ -674,8 +674,7 @@ func (handlers *Handlers) lookupEthAccountCode(r *http.Request) interface{} {
 
 func (handlers *Handlers) postBtcFormatUnit(r *http.Request) interface{} {
 	type response struct {
-		Success      bool   `json:"success"`
-		ErrorMessage string `json:"errorMessage,omitempty"`
+		Success bool `json:"success"`
 	}
 
 	var request struct {
diff --git a/frontends/web/src/api/coins.ts b/frontends/web/src/api/coins.ts
index 6362badf73..2a047353ac 100644
--- a/frontends/web/src/api/coins.ts
+++ b/frontends/web/src/api/coins.ts
@@ -16,7 +16,6 @@
 
 import { subscribeEndpoint, TSubscriptionCallback } from './subscribe';
 import type { CoinCode, Fiat } from './account';
-import type { ISuccess } from './backend';
 import { apiPost, apiGet } from '@/utils/request';
 
 export type BtcUnit = 'default' | 'sat';
@@ -34,7 +33,11 @@ export const subscribeCoinHeaders = (coinCode: CoinCode) => (
   )
 );
 
-export const setBtcUnit = (unit: BtcUnit): Promise<ISuccess> => {
+export type TSetBtcUnitResponse = {
+  success: boolean;
+};
+
+export const setBtcUnit = (unit: BtcUnit): Promise<TSetBtcUnitResponse> => {
   return apiPost('coins/btc/set-unit', { unit });
 };
 
diff --git a/frontends/web/src/components/amount/amount-with-unit.module.css b/frontends/web/src/components/amount/amount-with-unit.module.css
new file mode 100644
index 0000000000..d6437f6a21
--- /dev/null
+++ b/frontends/web/src/components/amount/amount-with-unit.module.css
@@ -0,0 +1,50 @@
+.rates {
+  cursor: default;
+  line-height: 1;
+  margin: 0;
+  white-space: nowrap;
+}
+
+.unit {
+  font-weight: 400;
+  color: var(--color-secondary);
+  font-variant: normal;
+  position: relative;
+  cursor: default;
+}
+
+.availableFiatAmount {
+  color: var(--color-default);
+  padding-right: var(--space-quarter) !important;
+  text-align: right;
+}
+
+.rotatable {
+  cursor: default;
+  font-size: inherit;
+  position: relative;
+}
+
+.rotatable::after {
+  border-bottom: dotted 1px var(--color-softblack);
+  content: "";
+  position: absolute;
+  bottom: 0;
+  left: 2px;
+  right: 2px;
+}
+
+.notAvailable {
+  color: var(--color-default);
+}
+
+@media (max-width: 880px) {
+  .fiatRow {
+    bottom: 0;
+  }
+
+  .availableFiatAmount,
+  .availableFiatUnit {
+    line-height: 24px;
+  }
+}
\ No newline at end of file
diff --git a/frontends/web/src/components/amount/amount-with-unit.tsx b/frontends/web/src/components/amount/amount-with-unit.tsx
new file mode 100644
index 0000000000..221c27c340
--- /dev/null
+++ b/frontends/web/src/components/amount/amount-with-unit.tsx
@@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020-2025 Shift Crypto AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useContext } from 'react';
+import { CoinUnit, ConversionUnit, IAmount } from '@/api/account';
+import { RatesContext } from '@/contexts/RatesContext';
+import { Amount } from '@/components/amount/amount';
+import { isBitcoinCoin } from '@/routes/account/utils';
+import style from './amount-with-unit.module.css';
+
+type TAmountWithUnitProps = {
+    amount: IAmount;
+    tableRow?: boolean;
+    enableRotateUnit?: boolean;
+    sign?: string;
+    removeBtcTrailingZeroes?: boolean;
+    alwaysShowAmounts?: boolean;
+    convertToFiat?: boolean;
+}
+
+export const AmountWithUnit = ({
+  amount,
+  tableRow,
+  enableRotateUnit: rotateUnit,
+  sign,
+  removeBtcTrailingZeroes,
+  convertToFiat,
+  alwaysShowAmounts = false
+}: TAmountWithUnitProps) => {
+  const { rotateDefaultCurrency, defaultCurrency, rotateBtcUnit } = useContext(RatesContext);
+
+  let displayedAmount: string = '';
+  let displayedUnit: CoinUnit | ConversionUnit;
+  let onClick: () => Promise<void>;
+
+  if (convertToFiat) {
+    // amount.conversions[defaultCurrency] can be empty in recent transactions.
+    if (amount?.conversions && !!amount.conversions[defaultCurrency]) {
+      displayedAmount = amount.conversions[defaultCurrency];
+    }
+    displayedUnit = defaultCurrency;
+    onClick = rotateDefaultCurrency;
+  } else {
+    displayedAmount = amount.amount;
+    displayedUnit = amount.unit;
+    onClick = rotateBtcUnit;
+  }
+
+  const enableClick = rotateUnit && (convertToFiat || isBitcoinCoin(amount.unit));
+  const formattedAmount = !!displayedAmount ?
+    (
+      <Amount
+        alwaysShowAmounts={alwaysShowAmounts}
+        amount={displayedAmount}
+        unit={displayedUnit}
+        removeBtcTrailingZeroes={!!removeBtcTrailingZeroes}
+        onMobileClick={enableClick ? onClick : undefined}
+      />
+    ) : '---';
+
+  const amountUnit = <AmountUnit unit={displayedUnit} rotateUnit={enableClick ? onClick : undefined}/>;
+
+  if (tableRow) {
+    return (
+      <tr className={style.fiatRow}>
+        <td className={style.availableFiatAmount}>{formattedAmount}</td>
+        <td>{amountUnit}</td>
+      </tr>
+    );
+  }
+  return (
+    <span className={ `${style.rates} ${!displayedAmount ? style.notAvailable : ''}`}>
+      {!!displayedAmount ? sign : ''}
+      {formattedAmount}
+      {' '}
+      {amountUnit}
+    </span>
+  );
+};
+
+type TAmountUnitProps = {
+  rotateUnit?: () => Promise<void>;
+  unit: ConversionUnit | CoinUnit;
+}
+
+export const AmountUnit = ({ rotateUnit, unit }: TAmountUnitProps) => {
+  const classRototable = rotateUnit ? style.rotatable : '';
+  const textStyle = `${style.unit} ${classRototable}`;
+  return <span className={textStyle} onClick={rotateUnit}>{unit}</span>;
+};
diff --git a/frontends/web/src/components/amount/amount.tsx b/frontends/web/src/components/amount/amount.tsx
index a8ac7b06ba..45482b7480 100644
--- a/frontends/web/src/components/amount/amount.tsx
+++ b/frontends/web/src/components/amount/amount.tsx
@@ -16,20 +16,11 @@
 
 import { useContext } from 'react';
 import { AppContext } from '@/contexts/AppContext';
-import { RatesContext } from '@/contexts/RatesContext';
 import { LocalizationContext } from '@/contexts/localization-context';
 import { useMediaQuery } from '@/hooks/mediaquery';
 import { CoinUnit, ConversionUnit } from '@/api/account';
 import style from './amount.module.css';
 
-type TProps = {
-  amount: string;
-  unit: CoinUnit | ConversionUnit;
-  removeBtcTrailingZeroes?: boolean;
-  alwaysShowAmounts?: boolean
-  allowRotateCurrencyOnMobile?: boolean;
-};
-
 const formatSats = (amount: string): JSX.Element => {
   const blocks: JSX.Element[] = [];
   const blockSize = 3;
@@ -92,21 +83,27 @@ const formatBtc = (
   );
 };
 
+type TProps = {
+  amount: string;
+  unit: CoinUnit | ConversionUnit;
+  removeBtcTrailingZeroes?: boolean;
+  alwaysShowAmounts?: boolean;
+  onMobileClick?: () => Promise<void>;
+};
+
 export const Amount = ({
   amount,
   unit,
   removeBtcTrailingZeroes,
   alwaysShowAmounts = false,
-  allowRotateCurrencyOnMobile = false,
+  onMobileClick,
 }: TProps) => {
-  const { rotateDefaultCurrency } = useContext(RatesContext);
   const isMobile = useMediaQuery('(max-width: 768px)');
 
   const handleClick = () => {
-    if (!isMobile || !allowRotateCurrencyOnMobile) {
-      return;
+    if (isMobile && onMobileClick) {
+      onMobileClick();
     }
-    rotateDefaultCurrency();
   };
 
   return (
diff --git a/frontends/web/src/components/balance/balance.module.css b/frontends/web/src/components/balance/balance.module.css
index 1356301695..f86c98f19f 100644
--- a/frontends/web/src/components/balance/balance.module.css
+++ b/frontends/web/src/components/balance/balance.module.css
@@ -1,63 +1,51 @@
 .balanceTable {
-    align-items: flex-end;
-    border-collapse: collapse;
-    border-spacing: 0;
-    display: flex;
-    flex-direction: row;
-    position: relative;
+  align-items: flex-end;
+  border-collapse: collapse;
+  border-spacing: 0;
+  display: flex;
+  flex-direction: row;
+  position: relative;
 }
 
 .balanceTable tr {
-    display: inline-block;
-    vertical-align: bottom;
+  display: inline-block;
+  vertical-align: bottom;
+  padding-right: var(--space-half);
 }
 
 .balanceTable td {
-    font-size: var(--size-large);
-    font-weight: 400;
-    line-height: 1.2;
-    padding: 0;
-    vertical-align: baseline;
+  font-size: var(--size-large);
+  font-weight: 400;
+  line-height: 1.2;
+  padding: 0;
+  vertical-align: baseline;
 }
 
 .balanceTable td:last-child {
-    font-size: var(--size-default);
-}
-
-.balanceTable .availableAmount {
-    color: var(--color-default);
-    padding-right: var(--space-quarter);
-    text-align: right;
-    white-space: nowrap;
-}
-
-.balanceTable .availableUnit {
-    color: var(--color-secondary);
-    padding-right: var(--space-half);
-    white-space: nowrap;
+  font-size: var(--size-default);
 }
 
 .pendingBalance {
-    color: var(--color-secondary);
-    font-size: var(--size-small);
-    line-height: 1;
-    margin: 0;
-    padding-top: var(--space-half);
+  color: var(--color-secondary);
+  font-size: var(--size-small);
+  line-height: 1;
+  margin: 0;
+  padding-top: var(--space-half);
 }
 
 @media (max-width: 768px) {
 
-    .balanceTable {
-        display: inline-block;
-        margin: 0 auto;
-    }
+  .balanceTable {
+    display: inline-block;
+    margin: 0 auto;
+  }
 
-    .balanceTable td {
-        font-size: var(--size-large-mobile);
-        line-height: 1.5;
-    }
+  .balanceTable td {
+    font-size: var(--size-large-mobile);
+    line-height: 1.5;
+  }
 
-    .pendingBalance {
-        padding-top: var(--space-quarter);
-    }
+  .pendingBalance {
+    padding-top: var(--space-quarter);
+  }
 }
\ No newline at end of file
diff --git a/frontends/web/src/components/balance/balance.test.tsx b/frontends/web/src/components/balance/balance.test.tsx
index 523b295ee4..85f714b58e 100644
--- a/frontends/web/src/components/balance/balance.test.tsx
+++ b/frontends/web/src/components/balance/balance.test.tsx
@@ -50,6 +50,28 @@ describe('components/balance/balance', () => {
         amount: '0.005',
         unit: 'BTC',
         estimated: false,
+        conversions: {
+          BTC: '0.005',
+          AUD: '512',
+          BRL: '512',
+          CAD: '512',
+          CHF: '512',
+          CNY: '512',
+          CZK: '512',
+          EUR: '512',
+          GBP: '512',
+          HKD: '512',
+          ILS: '512',
+          JPY: '512',
+          KRW: '512',
+          NOK: '512',
+          PLN: '512',
+          RUB: '512',
+          sat: '512',
+          SEK: '512',
+          SGD: '512',
+          USD: '512',
+        }
       },
       incoming: {
         amount: '0.003',
@@ -80,7 +102,7 @@ describe('components/balance/balance', () => {
       }
     };
     const { getByTestId } = render(<Balance balance={MOCK_BALANCE} />);
-    expect(getByTestId('availableBalance').textContent).toBe('0.005BTC');
+    expect(getByTestId('availableBalance').textContent).toBe('0.005BTC512USD');
     expect(getByTestId('incomingBalance').textContent).toBe('+0.003 BTC / 512 USD');
   });
 });
@@ -102,6 +124,28 @@ describe('components/balance/balance', () => {
         amount: '0.005',
         unit: 'BTC',
         estimated: false,
+        conversions: {
+          BTC: '0.005',
+          AUD: '512',
+          BRL: '512',
+          CAD: '512',
+          CHF: '512',
+          CNY: '512',
+          CZK: '512',
+          EUR: '512',
+          GBP: '512',
+          HKD: '512',
+          ILS: '512',
+          JPY: '512',
+          KRW: '512',
+          NOK: '512',
+          PLN: '512',
+          RUB: '512',
+          sat: '512',
+          SEK: '512',
+          SGD: '512',
+          USD: '512',
+        },
       },
       incoming: {
         amount: '0.003',
@@ -132,7 +176,7 @@ describe('components/balance/balance', () => {
       }
     };
     const { getByTestId } = render(<Balance balance={MOCK_BALANCE} />);
-    expect(getByTestId('availableBalance').textContent).toBe('0.005BTC');
+    expect(getByTestId('availableBalance').textContent).toBe('0.005BTC512USD');
     expect(getByTestId('incomingBalance').textContent).toBe('+0.003 BTC / 1,511.99 USD');
   });
   afterEach(() => {
diff --git a/frontends/web/src/components/balance/balance.tsx b/frontends/web/src/components/balance/balance.tsx
index f55c2f841a..b9a5ebd47b 100644
--- a/frontends/web/src/components/balance/balance.tsx
+++ b/frontends/web/src/components/balance/balance.tsx
@@ -17,9 +17,9 @@
 
 import { useTranslation } from 'react-i18next';
 import { IBalance } from '@/api/account';
-import { FiatConversion } from '@/components/rates/rates';
 import { Amount } from '@/components/amount/amount';
 import { BalanceSkeleton } from '@/components/balance/balance-skeleton';
+import { AmountWithUnit } from '@/components/amount/amount-with-unit';
 import style from './balance.module.css';
 
 type TProps = {
@@ -41,22 +41,19 @@ export const Balance = ({
   return (
     <header className={style.balance}>
       <table className={style.balanceTable}>
-        <tbody>
-          <tr data-testid="availableBalance">
-            <td className={style.availableAmount}>
-              <Amount
-                amount={balance.available.amount}
-                unit={balance.available.unit}
-                removeBtcTrailingZeroes
-              />
-            </td>
-            <td className={style.availableUnit}>{balance.available.unit}</td>
-          </tr>
-          <FiatConversion
+        <tbody data-testid="availableBalance">
+          <AmountWithUnit
             amount={balance.available}
             tableRow
-            noAction={noRotateFiat}
-            noBtcZeroes
+            enableRotateUnit={!noRotateFiat}
+            removeBtcTrailingZeroes
+          />
+          <AmountWithUnit
+            amount={balance.available}
+            tableRow
+            enableRotateUnit={!noRotateFiat}
+            removeBtcTrailingZeroes
+            convertToFiat
           />
         </tbody>
       </table>
@@ -73,7 +70,7 @@ export const Balance = ({
               {' '}{balance.incoming.unit} /
               <span className={style.incomingConversion}>
                 {' '}
-                <FiatConversion amount={balance.incoming} noBtcZeroes/>
+                <AmountWithUnit amount={balance.incoming} removeBtcTrailingZeroes convertToFiat/>
               </span>
             </span>
           </p>
diff --git a/frontends/web/src/components/rates/rates.module.css b/frontends/web/src/components/rates/rates.module.css
deleted file mode 100644
index e58f9c4d27..0000000000
--- a/frontends/web/src/components/rates/rates.module.css
+++ /dev/null
@@ -1,62 +0,0 @@
-.rates {
-    cursor: default;
-    line-height: 1;
-    margin: 0;
-    white-space: nowrap;
-}
-
-.unit {
-    font-weight: 400;
-}
-
-.unit,
-.unitAction {
-    color: var(--color-secondary);
-    font-size: var(--size-default);
-    font-variant: normal;
-    position: relative;
-}
-
-.availableFiatAmount {
-    color: var(--color-default);
-    padding-right: var(--space-quarter) !important;
-    text-align: right;
-}
-
-.availableFiatUnitNoAction,
-.availableFiatUnit {
-    color: var(--color-secondary);
-    cursor: default;
-    position: relative;
-}
-
-.rotatable {
-    cursor: default;
-    font-size: inherit;
-    position: relative;
-}
-
-.rotatable::after { 
-    border-bottom: dotted 1px var(--color-softblack);
-    content: "";
-    position: absolute;
-    bottom: 0;
-    left: 2px;
-    right: 2px;
-}
-
-.notAvailable {
-    color: var(--color-default);
-}
-
-@media (max-width: 880px) {
-    .fiatRow {
-        bottom: 0;
-    }
-
-    .availableFiatAmount,
-    .availableFiatUnit {
-        line-height: 24px;
-    }
-}
-
diff --git a/frontends/web/src/components/rates/rates.tsx b/frontends/web/src/components/rates/rates.tsx
deleted file mode 100644
index 0757938810..0000000000
--- a/frontends/web/src/components/rates/rates.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * Copyright 2018 Shift Devices AG
- * Copyright 2020 Shift Crypto AG
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { useContext } from 'react';
-import { ConversionUnit, IAmount } from '@/api/account';
-import { RatesContext } from '@/contexts/RatesContext';
-import { Amount } from '@/components/amount/amount';
-import style from './rates.module.css';
-
-type TProvidedProps = {
-    amount?: IAmount;
-    tableRow?: boolean;
-    unstyled?: boolean;
-    skipUnit?: boolean;
-    noAction?: boolean;
-    sign?: string;
-    noBtcZeroes?: boolean;
-    alwaysShowAmounts?: boolean;
-}
-
-const Conversion = ({
-  amount,
-  tableRow,
-  unstyled,
-  skipUnit,
-  noAction,
-  sign,
-  noBtcZeroes,
-  alwaysShowAmounts = false
-}: TProvidedProps) => {
-
-  const { defaultCurrency } = useContext(RatesContext);
-
-  let formattedAmount = <>{'---'}</>;
-  let isAvailable = false;
-
-  let activeUnit: ConversionUnit = defaultCurrency;
-
-  // amount.conversions[defaultCurrency] can be empty in recent transactions.
-  if (amount && amount.conversions && amount.conversions[defaultCurrency] && amount.conversions[defaultCurrency] !== '') {
-    isAvailable = true;
-    formattedAmount = (
-      <Amount
-        alwaysShowAmounts={alwaysShowAmounts}
-        amount={amount.conversions[defaultCurrency]}
-        unit={activeUnit}
-        removeBtcTrailingZeroes={!!noBtcZeroes}
-        allowRotateCurrencyOnMobile={!noAction}
-      />
-    );
-  }
-
-  if (tableRow) {
-    return (
-      <tr className={unstyled ? '' : style.fiatRow}>
-        <td className={unstyled ? '' : style.availableFiatAmount}>{formattedAmount}</td>
-        {
-          !noAction && (
-            <DefaultCurrencyRotator activeUnit={activeUnit} className={unstyled ? '' : style.availableFiatUnit} />
-          )
-        }
-        {
-          noAction && (
-            <td className={unstyled ? '' : style.availableFiatUnitNoAction}>{activeUnit}</td>
-          )
-        }
-      </tr>
-    );
-  }
-  return (
-    <span className={ `${style.rates} ${!isAvailable ? style.notAvailable : ''}`}>
-      {isAvailable ? sign : ''}
-      {formattedAmount}
-      {' '}
-      {
-        !skipUnit && !noAction && (
-          <DefaultCurrencyRotator activeUnit={activeUnit} tableRow={false} className={style.unitAction} />
-        )
-      }
-      {
-        !skipUnit && noAction && (
-          <span className={style.unit}>{activeUnit}</span>
-        )
-      }
-    </span>
-  );
-};
-
-type TDefaultCurrencyRotator = {
-  activeUnit?: ConversionUnit;
-  className?: string;
-  tableRow?: boolean;
-}
-
-export const DefaultCurrencyRotator = ({
-  activeUnit,
-  className = '',
-  tableRow = true
-}: TDefaultCurrencyRotator) => {
-  const { rotateDefaultCurrency, defaultCurrency } = useContext(RatesContext);
-  const displayedCurrency = activeUnit ? activeUnit : defaultCurrency;
-
-  const textStyle = `${className} ${style.rotatable}`;
-  if (!tableRow) {
-    return <span className={textStyle} onClick={rotateDefaultCurrency}>{displayedCurrency}</span>;
-  }
-  return <td className={textStyle} onClick={rotateDefaultCurrency}>{displayedCurrency}</td>;
-};
-
-
-export const FiatConversion = Conversion;
diff --git a/frontends/web/src/components/transactions/components/details-dialog.tsx b/frontends/web/src/components/transactions/components/details-dialog.tsx
index c71818085a..40916d9a2c 100644
--- a/frontends/web/src/components/transactions/components/details-dialog.tsx
+++ b/frontends/web/src/components/transactions/components/details-dialog.tsx
@@ -19,7 +19,6 @@ import { useTranslation } from 'react-i18next';
 import { ITransaction, IAmount, getTransaction, TTransactionStatus, TTransactionType } from '@/api/account';
 import { A } from '@/components/anchor/anchor';
 import { Dialog } from '@/components/dialog/dialog';
-import { FiatConversion } from '@/components/rates/rates';
 import { Amount } from '@/components/amount/amount';
 import { Note } from './note';
 import { TxDetail } from './detail';
@@ -28,6 +27,7 @@ import { TxDateDetail } from './date';
 import { TxStatusDetail } from './status';
 import { TxDetailCopyableValues } from './address-or-txid';
 import styles from './details.module.css';
+import { AmountWithUnit } from '@/components/amount/amount-with-unit';
 
 type TProps = {
   open: boolean;
@@ -110,18 +110,17 @@ export const TxDetailsDialog = ({
           <TxDateDetail time={time} />
           <TxDetail label={t('transaction.details.fiat')}>
             <span className={styles.fiat}>
-              <FiatConversion amount={amount} sign={sign} noAction />
-            </span>
-          </TxDetail>
-          <TxDetail label={t('transaction.details.fiatAtTime')}>
-            <span className={styles.fiat}>
-              {transactionInfo.amountAtTime?.estimated === false ?
-                <FiatConversion amount={transactionInfo.amountAtTime} sign={sign} noAction />
-                :
-                <FiatConversion noAction />
-              }
+              <AmountWithUnit amount={amount} sign={sign} convertToFiat/>
             </span>
           </TxDetail>
+          {transactionInfo.amountAtTime?.estimated === false &&
+          (
+            <TxDetail label={t('transaction.details.fiatAtTime')}>
+              <span className={styles.fiat}>
+                <AmountWithUnit amount={transactionInfo.amountAtTime} sign={sign} convertToFiat/>
+              </span>
+            </TxDetail>
+          )}
           <TxDetail label={t('transaction.details.amount')}>
             <span className={styles.amount}>
               {sign}
diff --git a/frontends/web/src/contexts/RatesContext.tsx b/frontends/web/src/contexts/RatesContext.tsx
index 889976643a..a7d2c67c1a 100644
--- a/frontends/web/src/contexts/RatesContext.tsx
+++ b/frontends/web/src/contexts/RatesContext.tsx
@@ -23,6 +23,7 @@ type RatesContextProps = {
     activeCurrencies: Fiat[];
     btcUnit?: BtcUnit;
     rotateDefaultCurrency: () => Promise<void>;
+    rotateBtcUnit: () => Promise<void>;
     addToActiveCurrencies: (fiat: Fiat) => Promise<void>;
     updateDefaultCurrency: (fiat: Fiat) => void;
     updateRatesConfig: () => Promise<void>;
diff --git a/frontends/web/src/contexts/RatesProvider.tsx b/frontends/web/src/contexts/RatesProvider.tsx
index 841ab95933..c2d411bacd 100644
--- a/frontends/web/src/contexts/RatesProvider.tsx
+++ b/frontends/web/src/contexts/RatesProvider.tsx
@@ -17,7 +17,7 @@
 import { ReactNode, useEffect, useState } from 'react';
 import { RatesContext } from './RatesContext';
 import { Fiat } from '@/api/account';
-import { BtcUnit } from '@/api/coins';
+import { BtcUnit, setBtcUnit as setBackendBtcUnit } from '@/api/coins';
 import { getConfig, setConfig } from '@/utils/config';
 import { reinitializeAccounts } from '@/api/backend';
 import { equal } from '@/utils/equal';
@@ -64,6 +64,16 @@ export const RatesProvider = ({ children }: TProps) => {
     setDefaultCurrency(fiat);
   };
 
+  const rotateBtcUnit = async () => {
+    const unit: BtcUnit = btcUnit === 'default' ? 'sat' : 'default';
+    await setConfig({ backend: { btcUnit: unit } });
+    setBtcUnit(unit);
+    const response = await setBackendBtcUnit(unit);
+    if (!response.success) {
+      console.log('setBackendBtcUnit failed.');
+    }
+  };
+
   // this is a method to select / add a currency
   // into the active currencies list
   const addToActiveCurrencies = async (fiat: Fiat) => {
@@ -97,7 +107,8 @@ export const RatesProvider = ({ children }: TProps) => {
         addToActiveCurrencies,
         updateDefaultCurrency,
         updateRatesConfig,
-        removeFromActiveCurrencies
+        removeFromActiveCurrencies,
+        rotateBtcUnit,
       }}
     >
       {children}
diff --git a/frontends/web/src/routes/account/account.tsx b/frontends/web/src/routes/account/account.tsx
index 52ceddcf1e..fc8e3b1af6 100644
--- a/frontends/web/src/routes/account/account.tsx
+++ b/frontends/web/src/routes/account/account.tsx
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useContext, useEffect, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { Link } from 'react-router-dom';
 import * as accountApi from '@/api/account';
@@ -54,6 +54,7 @@ import { Button } from '@/components/forms';
 import { SubTitle } from '@/components/title';
 import { TransactionHistorySkeleton } from '@/routes/account/transaction-history-skeleton';
 import style from './account.module.css';
+import { RatesContext } from '@/contexts/RatesContext';
 
 type Props = {
   accounts: accountApi.IAccount[];
@@ -68,6 +69,8 @@ export const Account = ({
 }: Props) => {
   const { t } = useTranslation();
 
+  const { btcUnit } = useContext(RatesContext);
+
   const [balance, setBalance] = useState<accountApi.IBalance>();
   const [status, setStatus] = useState<accountApi.IStatus>();
   const [syncedAddressesCount, setSyncedAddressesCount] = useState<number>();
@@ -195,6 +198,10 @@ export const Account = ({
     return () => unsubscribe(subscriptions);
   }, [code, onAccountChanged, onStatusChanged, status]);
 
+  useEffect(() => {
+    onAccountChanged(code, status);
+  }, [btcUnit, onAccountChanged, code, status]);
+
   const exportAccount = () => {
     if (status === undefined || status.fatalError) {
       return;
diff --git a/frontends/web/src/routes/account/send/utxos.tsx b/frontends/web/src/routes/account/send/utxos.tsx
index 72ea7585ac..2faa21b056 100644
--- a/frontends/web/src/routes/account/send/utxos.tsx
+++ b/frontends/web/src/routes/account/send/utxos.tsx
@@ -30,11 +30,11 @@ import { Dialog } from '@/components/dialog/dialog';
 import { Button, Checkbox } from '@/components/forms';
 import { ExternalLink } from '@/components/icon';
 import { Amount } from '@/components/amount/amount';
-import { FiatConversion } from '@/components/rates/rates';
 import { getScriptName } from '@/routes/account/utils';
 import { Message } from '@/components/message/message';
 import { Badge } from '@/components/badge/badge';
 import style from './utxos.module.css';
+import { AmountWithUnit } from '@/components/amount/amount-with-unit';
 
 export type TSelectedUTXOs = {
   [key: string]: boolean;
@@ -125,7 +125,7 @@ export const UTXOs = ({
                           {utxo.amount.unit}
                         </span>
                       </span>
-                      <FiatConversion alwaysShowAmounts amount={utxo.amount} unstyled noAction/>
+                      <AmountWithUnit alwaysShowAmounts amount={utxo.amount} convertToFiat/>
                     </div>
                     <div className={style.address}>
                       <span className={style.label}>
diff --git a/frontends/web/src/routes/account/summary/balancerow.tsx b/frontends/web/src/routes/account/summary/balancerow.tsx
index c2667a5563..0573d96d7c 100644
--- a/frontends/web/src/routes/account/summary/balancerow.tsx
+++ b/frontends/web/src/routes/account/summary/balancerow.tsx
@@ -23,8 +23,8 @@ import { useMediaQuery } from '@/hooks/mediaquery';
 import { Logo } from '@/components/icon/logo';
 import { Amount } from '@/components/amount/amount';
 import { AsciiSpinner } from '@/components/spinner/ascii';
-import { FiatConversion } from '@/components/rates/rates';
 import style from './accountssummary.module.css';
+import { AmountWithUnit } from '@/components/amount/amount-with-unit';
 
 type TNameColProps = {
   coinCode: CoinCode;
@@ -82,7 +82,7 @@ export const BalanceRow = (
           </span>
         </td>
         <td data-label={t('accountSummary.fiatBalance')}>
-          <FiatConversion amount={balance.available} noAction={true} />
+          <AmountWithUnit amount={balance.available} convertToFiat/>
         </td>
       </tr>
     );
diff --git a/frontends/web/src/routes/account/summary/chart.tsx b/frontends/web/src/routes/account/summary/chart.tsx
index 295bcd85b8..fbc04739e1 100644
--- a/frontends/web/src/routes/account/summary/chart.tsx
+++ b/frontends/web/src/routes/account/summary/chart.tsx
@@ -25,9 +25,9 @@ import { Amount } from '@/components/amount/amount';
 import { PercentageDiff } from './percentage-diff';
 import { Filters } from './filters';
 import { getDarkmode } from '@/components/darkmode/darkmode';
-import { DefaultCurrencyRotator } from '@/components/rates/rates';
 import { RatesContext } from '@/contexts/RatesContext';
 import { AppContext, TChartDisplay } from '@/contexts/AppContext';
+import { AmountUnit } from '@/components/amount/amount-with-unit';
 import styles from './chart.module.css';
 
 type TProps = {
@@ -138,7 +138,7 @@ export const Chart = ({
 
   const { t, i18n } = useTranslation();
   const { chartDisplay, setChartDisplay } = useContext(AppContext);
-  const { defaultCurrency } = useContext(RatesContext);
+  const { defaultCurrency, rotateDefaultCurrency } = useContext(RatesContext);
   const [searchParams] = useSearchParams();
 
   const ref = useRef<HTMLDivElement>(null);
@@ -582,13 +582,13 @@ export const Chart = ({
                 amount={!showMobileTotalValue ? formattedChartTotal : toolTipValue}
                 unit={chartFiat}
                 removeBtcTrailingZeroes
-                allowRotateCurrencyOnMobile
+                onMobileClick={rotateDefaultCurrency}
               />
             ) : (
               <Skeleton minWidth="220px" />
             )}
             <span className={styles.totalUnit}>
-              {chartTotal !== null && <DefaultCurrencyRotator tableRow={false} />}
+              {chartTotal !== null && <AmountUnit unit={chartFiat} rotateUnit={rotateDefaultCurrency}/>}
             </span>
           </div>
           {!showMobileTotalValue ? (
diff --git a/frontends/web/src/routes/account/summary/subtotalrow.tsx b/frontends/web/src/routes/account/summary/subtotalrow.tsx
index 8f3fb71249..bc658091a2 100644
--- a/frontends/web/src/routes/account/summary/subtotalrow.tsx
+++ b/frontends/web/src/routes/account/summary/subtotalrow.tsx
@@ -18,8 +18,8 @@ import { useTranslation } from 'react-i18next';
 import * as accountApi from '@/api/account';
 import { Logo } from '@/components/icon/logo';
 import { Amount } from '@/components/amount/amount';
-import { FiatConversion } from '@/components/rates/rates';
 import style from './accountssummary.module.css';
+import { AmountWithUnit } from '@/components/amount/amount-with-unit';
 
 type TProps = {
   balance?: accountApi.IAmount;
@@ -59,7 +59,7 @@ export const SubTotalRow = ({ coinCode, coinName, balance }: TProps) => {
       </td>
       <td data-label={t('accountSummary.fiatBalance')}>
         <strong>
-          <FiatConversion amount={balance} noAction={true} />
+          <AmountWithUnit amount={balance} convertToFiat/>
         </strong>
       </td>
     </tr>
@@ -92,7 +92,7 @@ export const SubTotalCoinRow = ({ coinCode, coinName, balance }: TProps) => {
         </span>
       </td>
       <td data-label={t('accountSummary.fiatBalance')}>
-        <FiatConversion amount={balance} noAction={true} />
+        <AmountWithUnit amount={balance} convertToFiat/>
       </td>
     </tr>
   );
diff --git a/frontends/web/src/routes/settings/components/appearance/displaySatsToggleSetting.tsx b/frontends/web/src/routes/settings/components/appearance/displaySatsToggleSetting.tsx
deleted file mode 100644
index 2d06d62756..0000000000
--- a/frontends/web/src/routes/settings/components/appearance/displaySatsToggleSetting.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * Copyright 2023 Shift Crypto AG
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { useContext, useEffect, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-import { Toggle } from '@/components/toggle/toggle';
-import { SettingsItem } from '@/routes/settings/components/settingsItem/settingsItem';
-import { useLoad } from '@/hooks/api';
-import { getConfig, setConfig } from '@/utils/config';
-import { RatesContext } from '@/contexts/RatesContext';
-import { BtcUnit, setBtcUnit } from '@/api/coins';
-import { alertUser } from '@/components/alert/Alert';
-
-export const DisplaySatsToggleSetting = () => {
-  const { t } = useTranslation();
-  const fetchedConfig = useLoad(getConfig);
-  const [displayAsSAT, setDisplayAsSAT] = useState<boolean>();
-
-  const { updateRatesConfig } = useContext(RatesContext);
-
-  useEffect(() => {
-    if (fetchedConfig) {
-      setDisplayAsSAT(fetchedConfig.backend.btcUnit === 'sat');
-    }
-  }, [fetchedConfig]);
-
-  const handleToggleSatsUnit = async () => {
-    const toggleDdisplayAsSAT = !displayAsSAT;
-    const unit: BtcUnit = toggleDdisplayAsSAT ? 'sat' : 'default';
-    setDisplayAsSAT(toggleDdisplayAsSAT);
-
-    await setConfig({
-      backend: { btcUnit: unit }
-    });
-
-    updateRatesConfig();
-
-    const result = await setBtcUnit(unit);
-
-    if (!result.success) {
-      alertUser(t('genericError'));
-    }
-  };
-
-  return (
-    <>
-      <SettingsItem
-        settingName={t('settings.expert.useSats')}
-        secondaryText={t('newSettings.appearance.toggleSats.description')}
-        extraComponent={
-          <>
-            {
-              displayAsSAT !== undefined ?
-                (
-                  <Toggle
-                    checked={displayAsSAT}
-                    onChange={handleToggleSatsUnit}
-                  />
-                ) :
-                null
-            }
-          </>
-        }
-      />
-    </>
-  );
-};
\ No newline at end of file
diff --git a/frontends/web/src/routes/settings/general.tsx b/frontends/web/src/routes/settings/general.tsx
index 1ce46d1f85..9c310a89be 100644
--- a/frontends/web/src/routes/settings/general.tsx
+++ b/frontends/web/src/routes/settings/general.tsx
@@ -21,7 +21,6 @@ import { DarkmodeToggleSetting } from './components/appearance/darkmodeToggleSet
 import { NotesImport } from './components/appearance/notesImport';
 import { NotesExport } from './components/appearance/notesExport';
 import { DefaultCurrencyDropdownSetting } from './components/appearance/defaultCurrencyDropdownSetting';
-import { DisplaySatsToggleSetting } from './components/appearance/displaySatsToggleSetting';
 import { LanguageDropdownSetting } from './components/appearance/languageDropdownSetting';
 import { ActiveCurrenciesDropdownSetting } from './components/appearance/activeCurrenciesDropdownSetting';
 import { WithSettingsTabs } from './components/tabs';
@@ -60,7 +59,6 @@ export const General = ({ devices, hasAccounts }: TPagePropsWithSettingsTabs) =>
                 <DefaultCurrencyDropdownSetting />
                 <ActiveCurrenciesDropdownSetting />
                 <DarkmodeToggleSetting />
-                <DisplaySatsToggleSetting />
                 { hasAccounts ? (
                   <>
                     <SubTitle className="m-top-default">


Reviews:
user: login: thisconnect
body: very nice thank you @Beerosagos !

I found a small bug with incoming amount unit see comment. 
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T17:27:20Z
user: login: Beerosagos
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-21T11:16:22Z
user: login: thisconnect
body: partly tested LGTM with tiny nit
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-24T11:06:46Z

Review comments:
diff_hunk: @@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020 Shift Crypto AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useContext } from 'react';
+import { CoinUnit, ConversionUnit, IAmount } from '@/api/account';
+import { RatesContext } from '@/contexts/RatesContext';
+import { Amount } from '@/components/amount/amount';
+import { isBitcoinCoin } from '@/routes/account/utils';
+import style from './amount-with-unit.module.css';
+
+type TFiatAmountWithUnitProps = {
path: frontends/web/src/components/amount/amount-with-unit.tsx
user: login: thisconnect
body: nit: probably a leftover, in other places we just use `TProps`, but here are 2 components, so consider changing to `TAmountWithUnitProps`?
created_at: 2025-03-12T13:11:17Z
author_association: COLLABORATOR
diff_hunk: @@ -64,6 +64,16 @@ export const RatesProvider = ({ children }: TProps) => {
     setDefaultCurrency(fiat);
   };
 
+  const rotateBtcUnit = async () => {
+    const unit: BtcUnit = btcUnit == 'default' ? 'sat' : 'default';
+    await setConfig({ backend: { btcUnit: unit } });
+    setBtcUnit(unit);
+    const response = await setBackendBtcUnit(unit);
+    if (!response.success) {
+      console.log('Error code [' + response.errorCode + ']: ' + response.errorMessage);
path: frontends/web/src/contexts/RatesProvider.tsx
user: login: thisconnect
body: Is errorCode expected? The api return type in `@/api/coins` might not be accurate, but I can't find the errorCode here https://github.com/BitBoxSwiss/bitbox-wallet-app/blob/da8e47601924371fd8f25c0410095a281c971a7e/backend/handlers/handlers.go#L670-L705


created_at: 2025-03-12T13:22:32Z
author_association: COLLABORATOR
diff_hunk: @@ -110,18 +110,17 @@ export const TxDetailsDialog = ({
           <TxDateDetail time={time} />
           <TxDetail label={t('transaction.details.fiat')}>
             <span className={styles.fiat}>
-              <FiatConversion amount={amount} sign={sign} noAction />
-            </span>
-          </TxDetail>
-          <TxDetail label={t('transaction.details.fiatAtTime')}>
-            <span className={styles.fiat}>
-              {transactionInfo.amountAtTime?.estimated == false ?
-                <FiatConversion amount={transactionInfo.amountAtTime} sign={sign} noAction />
-                :
-                <FiatConversion noAction />
-              }
+              <AmountWithUnit amount={amount} sign={sign} convertToFiat/>
             </span>
           </TxDetail>
+          {transactionInfo.amountAtTime?.estimated == false &&
path: frontends/web/src/components/transactions/components/details-dialog.tsx
user: login: thisconnect
body: nit: this was already before, if possible we should always use `===` 
created_at: 2025-03-12T13:28:38Z
author_association: COLLABORATOR
diff_hunk: @@ -82,7 +82,7 @@ export const BalanceRow = (
           </span>
         </td>
         <td data-label={t('accountSummary.fiatBalance')}>
-          <FiatConversion amount={balance.available} noAction={true} />
+          <AmountWithUnit amount={balance.available} convertToFiat/>
path: frontends/web/src/routes/account/summary/balancerow.tsx
user: login: thisconnect
body: Small regression when sending, I believe due to the `unstyled` prop that was there before.

before this commit:
<img width="320" alt="Screenshot 2025-03-12 at 12 14 23" src="https://github.com/user-attachments/assets/132c2099-3547-49a9-b029-5fed1bb699f7" />

with this commit the BTC unit is very large:
<img width="411" alt="Screenshot 2025-03-12 at 12 14 00" src="https://github.com/user-attachments/assets/181eb2b5-549f-4936-9613-82a56c01f418" />



 

 
created_at: 2025-03-12T14:43:44Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020 Shift Crypto AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useContext } from 'react';
+import { CoinUnit, ConversionUnit, IAmount } from '@/api/account';
+import { RatesContext } from '@/contexts/RatesContext';
+import { Amount } from '@/components/amount/amount';
+import { isBitcoinCoin } from '@/routes/account/utils';
+import style from './amount-with-unit.module.css';
+
+type TFiatAmountWithUnitProps = {
+    amount: IAmount;
+    tableRow?: boolean;
+    rotateUnit?: boolean;
path: frontends/web/src/components/amount/amount-with-unit.tsx
user: login: thisconnect
body: in AmountUnit component the `rotateUnit` accepts a callback, but here rotateUnit is a boolean.

Please change the name to `enableRotateUnit?: boolean` or similar.
created_at: 2025-03-12T14:49:00Z
author_association: COLLABORATOR
diff_hunk: @@ -41,22 +41,19 @@ export const Balance = ({
   return (
     <header className={style.balance}>
       <table className={style.balanceTable}>
path: frontends/web/src/components/balance/balance.tsx
user: login: thisconnect
body: reminder for myself: replace this table element and use div's
created_at: 2025-03-12T14:51:46Z
author_association: COLLABORATOR
diff_hunk: @@ -25,10 +25,10 @@ import { Amount } from '@/components/amount/amount';
 import { PercentageDiff } from './percentage-diff';
 import { Filters } from './filters';
 import { getDarkmode } from '@/components/darkmode/darkmode';
-import { DefaultCurrencyRotator } from '@/components/rates/rates';
 import { RatesContext } from '@/contexts/RatesContext';
 import { AppContext, TChartDisplay } from '@/contexts/AppContext';
 import styles from './chart.module.css';
+import { AmountUnit } from '@/components/amount/amount-with-unit';
path: frontends/web/src/routes/account/summary/chart.tsx
user: login: thisconnect
body: nit: if you touch these files anyways, please move the css import always last
created_at: 2025-03-12T16:11:33Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020 Shift Crypto AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useContext } from 'react';
+import { CoinUnit, ConversionUnit, IAmount } from '@/api/account';
+import { RatesContext } from '@/contexts/RatesContext';
+import { Amount } from '@/components/amount/amount';
+import { isBitcoinCoin } from '@/routes/account/utils';
+import style from './amount-with-unit.module.css';
+
+type TFiatAmountWithUnitProps = {
+    amount: IAmount;
+    tableRow?: boolean;
+    rotateUnit?: boolean;
+    sign?: string;
+    removeBtcTrailingZeroes?: boolean;
+    alwaysShowAmounts?: boolean;
+    convertToFiat?: boolean;
+}
+
+export const AmountWithUnit = ({
+  amount,
+  tableRow,
+  rotateUnit,
+  sign,
+  removeBtcTrailingZeroes,
+  convertToFiat,
+  alwaysShowAmounts = false
+}: TFiatAmountWithUnitProps) => {
+  const { rotateDefaultCurrency, defaultCurrency, rotateBtcUnit } = useContext(RatesContext);
+
+  let displayedAmount: string = '';
+  let displayedUnit: CoinUnit | ConversionUnit;
+  let onClick: () => Promise<void>;
+
+  if (convertToFiat) {
+    // amount.conversions[defaultCurrency] can be empty in recent transactions.
+    if (amount?.conversions && !!amount.conversions[defaultCurrency]) {
+      displayedAmount = amount.conversions[defaultCurrency];
+    }
+    displayedUnit = defaultCurrency;
+    onClick = rotateDefaultCurrency;
+  } else {
+    displayedAmount = amount.amount;
+    displayedUnit = amount.unit;
+    onClick = rotateBtcUnit;
+  }
+
+  const enableClick = rotateUnit && (convertToFiat || isBitcoinCoin(amount.unit));
+  const formattedAmount = !!displayedAmount ?
+    (
+      <Amount
+        alwaysShowAmounts={alwaysShowAmounts}
+        amount={displayedAmount}
+        unit={displayedUnit}
+        removeBtcTrailingZeroes={!!removeBtcTrailingZeroes}
+        onMobileClick={enableClick ? onClick : undefined}
+      />
+    ) : <>{'---'}</>;
+
+  const amountUnit = <AmountUnit unit={displayedUnit} rotateUnit={enableClick ? onClick : undefined}/>;
+
+  if (tableRow) {
+    return (
+      <tr className={style.fiatRow}>
+        <td className={style.availableFiatAmount}>{formattedAmount}</td>
+        <td>{amountUnit}</td>
+      </tr>
+    );
+  }
+  return (
+    <span className={ `${style.rates} ${!displayedAmount ? style.notAvailable : ''}`}>
+      {!!displayedAmount ? sign : ''}
+      {formattedAmount}
+      {' '}
+      {amountUnit}
+    </span>
+  );
+};
+
+type TAmountUnitProps = {
+  rotateUnit?: () => Promise<void>;
+  unit: ConversionUnit | CoinUnit;
+}
+
+export const AmountUnit = ({ rotateUnit, unit }: TAmountUnitProps) => {
+  const classRototable = rotateUnit ? `${style.rotatable}` : '';
path: frontends/web/src/components/amount/amount-with-unit.tsx
user: login: thisconnect
body: ```suggestion
  const classRototable = rotateUnit ? style.rotatable : '';
```
created_at: 2025-03-12T16:53:41Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020 Shift Crypto AG
path: frontends/web/src/components/amount/amount-with-unit.tsx
user: login: thisconnect
body: 2020-2025
created_at: 2025-03-12T17:12:50Z
author_association: COLLABORATOR
diff_hunk: @@ -92,21 +83,27 @@ const formatBtc = (
   );
 };
 
+type TProps = {
+  amount: string;
+  unit: CoinUnit | ConversionUnit;
+  removeBtcTrailingZeroes?: boolean;
+  alwaysShowAmounts?: boolean
+  onMobileClick?: () => Promise<void>;
+};
+
 export const Amount = ({
   amount,
   unit,
   removeBtcTrailingZeroes,
   alwaysShowAmounts = false,
-  allowRotateCurrencyOnMobile = false,
+  onMobileClick,
path: frontends/web/src/components/amount/amount.tsx
user: login: thisconnect
body: 👍 
created_at: 2025-03-12T17:15:30Z
author_association: COLLABORATOR
diff_hunk: @@ -73,7 +70,7 @@ export const Balance = ({
               {' '}{balance.incoming.unit} /
               <span className={style.incomingConversion}>
                 {' '}
-                <FiatConversion amount={balance.incoming} noBtcZeroes/>
+                <AmountWithUnit amount={balance.incoming} removeBtcTrailingZeroes convertToFiat/>
path: frontends/web/src/components/balance/balance.tsx
user: login: thisconnect
body: Small regression when sending, I believe due to the `unstyled` prop that was there before.

before this commit:
<img width="320" alt="Screenshot 2025-03-12 at 12 14 23" src="https://github.com/user-attachments/assets/132c2099-3547-49a9-b029-5fed1bb699f7" />

with this commit the BTC unit is very large:
<img width="411" alt="Screenshot 2025-03-12 at 12 14 00" src="https://github.com/user-attachments/assets/181eb2b5-549f-4936-9613-82a56c01f418" />



 

 
created_at: 2025-03-12T17:19:18Z
author_association: COLLABORATOR
diff_hunk: @@ -92,21 +83,27 @@ const formatBtc = (
   );
 };
 
+type TProps = {
+  amount: string;
+  unit: CoinUnit | ConversionUnit;
+  removeBtcTrailingZeroes?: boolean;
+  alwaysShowAmounts?: boolean
path: frontends/web/src/components/amount/amount.tsx
user: login: thisconnect
body: ```suggestion
  alwaysShowAmounts?: boolean;
```
created_at: 2025-03-12T17:20:09Z
author_association: COLLABORATOR
diff_hunk: @@ -73,7 +70,7 @@ export const Balance = ({
               {' '}{balance.incoming.unit} /
               <span className={style.incomingConversion}>
                 {' '}
-                <FiatConversion amount={balance.incoming} noBtcZeroes/>
+                <AmountWithUnit amount={balance.incoming} removeBtcTrailingZeroes convertToFiat/>
path: frontends/web/src/components/balance/balance.tsx
user: login: Beerosagos
body: Nice catch! Thanks :pray: 
created_at: 2025-03-21T11:16:22Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 Shift Devices AG
+ * Copyright 2020-2025 Shift Crypto AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useContext } from 'react';
+import { CoinUnit, ConversionUnit, IAmount } from '@/api/account';
+import { RatesContext } from '@/contexts/RatesContext';
+import { Amount } from '@/components/amount/amount';
+import { isBitcoinCoin } from '@/routes/account/utils';
+import style from './amount-with-unit.module.css';
+
+type TAmountWithUnitProps = {
+    amount: IAmount;
+    tableRow?: boolean;
+    enableRotateUnit?: boolean;
+    sign?: string;
+    removeBtcTrailingZeroes?: boolean;
+    alwaysShowAmounts?: boolean;
+    convertToFiat?: boolean;
+}
+
+export const AmountWithUnit = ({
+  amount,
+  tableRow,
+  enableRotateUnit: rotateUnit,
+  sign,
+  removeBtcTrailingZeroes,
+  convertToFiat,
+  alwaysShowAmounts = false
+}: TAmountWithUnitProps) => {
+  const { rotateDefaultCurrency, defaultCurrency, rotateBtcUnit } = useContext(RatesContext);
+
+  let displayedAmount: string = '';
+  let displayedUnit: CoinUnit | ConversionUnit;
+  let onClick: () => Promise<void>;
+
+  if (convertToFiat) {
+    // amount.conversions[defaultCurrency] can be empty in recent transactions.
+    if (amount?.conversions && !!amount.conversions[defaultCurrency]) {
+      displayedAmount = amount.conversions[defaultCurrency];
+    }
+    displayedUnit = defaultCurrency;
+    onClick = rotateDefaultCurrency;
+  } else {
+    displayedAmount = amount.amount;
+    displayedUnit = amount.unit;
+    onClick = rotateBtcUnit;
+  }
+
+  const enableClick = rotateUnit && (convertToFiat || isBitcoinCoin(amount.unit));
+  const formattedAmount = !!displayedAmount ?
+    (
+      <Amount
+        alwaysShowAmounts={alwaysShowAmounts}
+        amount={displayedAmount}
+        unit={displayedUnit}
+        removeBtcTrailingZeroes={!!removeBtcTrailingZeroes}
+        onMobileClick={enableClick ? onClick : undefined}
+      />
+    ) : <>{'---'}</>;
path: frontends/web/src/components/amount/amount-with-unit.tsx
user: login: thisconnect
body: ```suggestion
    ) : '---';
```
created_at: 2025-03-24T09:59:20Z
author_association: COLLABORATOR

PR comments:
user: login: Beerosagos
created_at: 2025-03-21T10:29:20Z
body: Rebased ^
author_association: COLLABORATOR
user: login: Beerosagos
created_at: 2025-03-21T11:17:27Z
body: @thisconnect thanks for the review! I updated the PR, PTAL :pray: 
author_association: COLLABORATOR
user: login: Beerosagos
created_at: 2025-03-25T11:24:45Z
body: rebased ^
author_association: COLLABORATOR