gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 03/03: fix wrong fee calculation


From: gnunet
Subject: [taler-wallet-core] 03/03: fix wrong fee calculation
Date: Fri, 20 Jan 2023 19:45:17 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 03b12d2b27e9d4038e2b02b303a0401160ebc632
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Jan 20 15:44:53 2023 -0300

    fix wrong fee calculation
---
 .../src/cta/InvoiceCreate/views.tsx                |  12 +-
 .../src/cta/Payment/views.tsx                      |  19 +-
 .../src/cta/TransferCreate/views.tsx               |   8 +-
 .../src/cta/Withdraw/views.tsx                     |  17 +-
 .../src/platform/chrome.ts                         |  34 +-
 .../src/wallet/Transaction.tsx                     | 453 ++++++++++-----------
 6 files changed, 260 insertions(+), 283 deletions(-)

diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
index 71227ace1..e96ee0705 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { LogoHeader } from "../../components/LogoHeader.js";
@@ -27,7 +28,11 @@ import { useTranslationContext } from 
"../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { TextField } from "../../mui/TextField.js";
 import editIcon from "../../svg/edit_24px.svg";
-import { ExchangeDetails, InvoiceDetails } from "../../wallet/Transaction.js";
+import {
+  ExchangeDetails,
+  getAmountWithFee,
+  InvoiceDetails,
+} from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
 export function ReadyView({
@@ -144,10 +149,7 @@ export function ReadyView({
           title={i18n.str`Details`}
           text={
             <InvoiceDetails
-              amount={{
-                effective: toBeReceived,
-                raw: requestAmount,
-              }}
+              amount={getAmountWithFee(toBeReceived, requestAmount, "credit")}
             />
           }
         />
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
index 244ac5886..53bc0c95f 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
@@ -27,7 +27,11 @@ import { PaymentButtons } from 
"../../components/PaymentButtons.js";
 import { SuccessBox, WarningBox } from "../../components/styled/index.js";
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { MerchantDetails, PurchaseDetails } from "../../wallet/Transaction.js";
+import {
+  getAmountWithFee,
+  MerchantDetails,
+  PurchaseDetails,
+} from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
 type SupportedStates =
@@ -41,13 +45,10 @@ export function BaseView(state: SupportedStates): VNode {
 
   const contractTerms: ContractTerms = state.payStatus.contractTerms;
 
-  const price = {
-    raw: state.amount,
-    effective:
-      "amountEffective" in state.payStatus
-        ? Amounts.parseOrThrow(state.payStatus.amountEffective)
-        : state.amount,
-  };
+  const effective =
+    "amountEffective" in state.payStatus
+      ? Amounts.parseOrThrow(state.payStatus.amountEffective)
+      : state.amount;
 
   return (
     <Fragment>
@@ -68,7 +69,7 @@ export function BaseView(state: SupportedStates): VNode {
           title={i18n.str`Details`}
           text={
             <PurchaseDetails
-              price={price}
+              price={getAmountWithFee(effective, state.amount, "debit")}
               info={{
                 ...contractTerms,
                 orderId: contractTerms.order_id,
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
index 373af8f74..a28b13141 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
@@ -23,7 +24,7 @@ import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { TextField } from "../../mui/TextField.js";
-import { TransferDetails } from "../../wallet/Transaction.js";
+import { getAmountWithFee, TransferDetails } from 
"../../wallet/Transaction.js";
 import { State } from "./index.js";
 
 export function ReadyView({
@@ -114,10 +115,7 @@ export function ReadyView({
           title={i18n.str`Details`}
           text={
             <TransferDetails
-              amount={{
-                effective: toBeReceived,
-                raw: debitAmount,
-              }}
+              amount={getAmountWithFee(debitAmount, toBeReceived, "debit")}
             />
           }
         />
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index 1cc87547e..4fb65f06c 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { ExchangeTosStatus } from "@gnu-taler/taler-util";
+import { Amounts, ExchangeTosStatus } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Amount } from "../../components/Amount.js";
@@ -26,7 +26,11 @@ import { TermsOfService } from 
"../../components/TermsOfService/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import editIcon from "../../svg/edit_24px.svg";
-import { ExchangeDetails, WithdrawDetails } from "../../wallet/Transaction.js";
+import {
+  ExchangeDetails,
+  getAmountWithFee,
+  WithdrawDetails,
+} from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
 export function SuccessView(state: State.Success): VNode {
@@ -64,10 +68,11 @@ export function SuccessView(state: State.Success): VNode {
           title={i18n.str`Details`}
           text={
             <WithdrawDetails
-              amount={{
-                effective: state.toBeReceived,
-                raw: state.chosenAmount,
-              }}
+              amount={getAmountWithFee(
+                state.toBeReceived,
+                state.chosenAmount,
+                "credit",
+              )}
             />
           }
         />
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 23730c2d3..1c5d5532e 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -584,26 +584,26 @@ function setAlertedIcon(): void {
 
 interface OffscreenCanvasRenderingContext2D
   extends CanvasState,
-  CanvasTransform,
-  CanvasCompositing,
-  CanvasImageSmoothing,
-  CanvasFillStrokeStyles,
-  CanvasShadowStyles,
-  CanvasFilters,
-  CanvasRect,
-  CanvasDrawPath,
-  CanvasUserInterface,
-  CanvasText,
-  CanvasDrawImage,
-  CanvasImageData,
-  CanvasPathDrawingStyles,
-  CanvasTextDrawingStyles,
-  CanvasPath {
+    CanvasTransform,
+    CanvasCompositing,
+    CanvasImageSmoothing,
+    CanvasFillStrokeStyles,
+    CanvasShadowStyles,
+    CanvasFilters,
+    CanvasRect,
+    CanvasDrawPath,
+    CanvasUserInterface,
+    CanvasText,
+    CanvasDrawImage,
+    CanvasImageData,
+    CanvasPathDrawingStyles,
+    CanvasTextDrawingStyles,
+    CanvasPath {
   readonly canvas: OffscreenCanvas;
 }
 declare const OffscreenCanvasRenderingContext2D: {
   prototype: OffscreenCanvasRenderingContext2D;
-  new(): OffscreenCanvasRenderingContext2D;
+  new (): OffscreenCanvasRenderingContext2D;
 };
 
 interface OffscreenCanvas extends EventTarget {
@@ -616,7 +616,7 @@ interface OffscreenCanvas extends EventTarget {
 }
 declare const OffscreenCanvas: {
   prototype: OffscreenCanvas;
-  new(width: number, height: number): OffscreenCanvas;
+  new (width: number, height: number): OffscreenCanvas;
 };
 
 function createCanvas(size: number): OffscreenCanvas {
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index c9e7bbe85..94d853d9a 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -28,17 +28,13 @@ import {
   stringifyPaytoUri,
   TalerProtocolTimestamp,
   Transaction,
-  TransactionDeposit,
-  TransactionRefresh,
-  TransactionRefund,
-  TransactionTip,
   TransactionType,
   TranslatedString,
   WithdrawalType,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { styled } from "@linaria/react";
-import { differenceInSeconds, isAfter, isFuture, isPast } from "date-fns";
+import { differenceInSeconds, isPast } from "date-fns";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import emptyImg from "../../static/img/empty.png";
@@ -68,6 +64,7 @@ import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { SafeHandler } from "../mui/handlers.js";
 import { Pages } from "../NavigationBar.js";
+import { assertUnreachable } from "../utils/index.js";
 
 interface Props {
   tid: string;
@@ -392,9 +389,10 @@ export function TransactionView({
   const { i18n } = useTranslationContext();
   const { safely } = useAlertContext();
 
+  const raw = Amounts.parseOrThrow(transaction.amountRaw);
+  const effective = Amounts.parseOrThrow(transaction.amountEffective);
+
   if (transaction.type === TransactionType.Withdrawal) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
-    const chosen = Amounts.parseOrThrow(transaction.amountRaw);
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -406,7 +404,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Withdrawal`}
-          total={total}
+          total={effective}
           kind="positive"
         >
           {transaction.exchangeBaseUrl}
@@ -417,7 +415,7 @@ export function TransactionView({
             .type === WithdrawalType.ManualTransfer ? (
           <Fragment>
             <BankDetailsByPaytoType
-              amount={chosen}
+              amount={raw}
               exchangeBaseUrl={transaction.exchangeBaseUrl}
               payto={parsePaytoUri(
                 transaction.withdrawalDetails.exchangePaytoUris[0],
@@ -500,10 +498,7 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <WithdrawDetails
-              amount={{
-                effective: Amounts.parseOrThrow(transaction.amountEffective),
-                raw: Amounts.parseOrThrow(transaction.amountRaw),
-              }}
+              amount={getAmountWithFee(effective, raw, "credit")}
             />
           }
         />
@@ -517,15 +512,9 @@ export function TransactionView({
         ? undefined
         : Amounts.parseOrThrow(transaction.refundPending);
 
-    const price = {
-      raw: Amounts.parseOrThrow(transaction.amountRaw),
-      effective: Amounts.parseOrThrow(transaction.amountEffective),
-    };
-    const refund = {
-      raw: Amounts.parseOrThrow(transaction.totalRefundRaw),
-      effective: Amounts.parseOrThrow(transaction.totalRefundEffective),
-    };
-    const total = Amounts.sub(price.effective, refund.effective).amount;
+    const effectiveRefund = Amounts.parseOrThrow(
+      transaction.totalRefundEffective,
+    );
 
     return (
       <TransactionTemplate
@@ -537,7 +526,7 @@ export function TransactionView({
       >
         <Header
           timestamp={transaction.timestamp}
-          total={total}
+          total={effective}
           type={i18n.str`Payment`}
           kind="negative"
         >
@@ -632,8 +621,8 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <PurchaseDetails
-              price={price}
-              refund={refund}
+              price={getAmountWithFee(effective, raw, "debit")}
+              effectiveRefund={effectiveRefund}
               info={transaction.info}
               proposalId={transaction.proposalId}
             />
@@ -645,7 +634,6 @@ export function TransactionView({
   }
 
   if (transaction.type === TransactionType.Deposit) {
-    const total = Amounts.parseOrThrow(transaction.amountRaw);
     const payto = parsePaytoUri(transaction.targetPaytoUri);
 
     const wireTime = AbsoluteTime.fromTimestamp(
@@ -663,7 +651,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Deposit`}
-          total={total}
+          total={effective}
           kind="negative"
         >
           {!payto ? transaction.targetPaytoUri : <NicePayto payto={payto} />}
@@ -671,7 +659,11 @@ export function TransactionView({
         {payto && <PartPayto payto={payto} kind="neutral" />}
         <Part
           title={i18n.str`Details`}
-          text={<DepositDetails transaction={transaction} />}
+          text={
+            <DepositDetails
+              amount={getAmountWithFee(effective, raw, "debit")}
+            />
+          }
           kind="neutral"
         />
         {!shouldBeWired ? (
@@ -712,11 +704,6 @@ export function TransactionView({
   }
 
   if (transaction.type === TransactionType.Refresh) {
-    const total = Amounts.sub(
-      Amounts.parseOrThrow(transaction.amountRaw),
-      Amounts.parseOrThrow(transaction.amountEffective),
-    ).amount;
-
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -728,22 +715,24 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Refresh`}
-          total={total}
+          total={effective}
           kind="negative"
         >
           {transaction.exchangeBaseUrl}
         </Header>
         <Part
           title={i18n.str`Details`}
-          text={<RefreshDetails transaction={transaction} />}
+          text={
+            <RefreshDetails
+              amount={getAmountWithFee(effective, raw, "debit")}
+            />
+          }
         />
       </TransactionTemplate>
     );
   }
 
   if (transaction.type === TransactionType.Tip) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
-
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -755,7 +744,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Tip`}
-          total={total}
+          total={effective}
           kind="positive"
         >
           {transaction.merchantBaseUrl}
@@ -767,14 +756,15 @@ export function TransactionView({
         /> */}
         <Part
           title={i18n.str`Details`}
-          text={<TipDetails transaction={transaction} />}
+          text={
+            <TipDetails amount={getAmountWithFee(effective, raw, "credit")} />
+          }
         />
       </TransactionTemplate>
     );
   }
 
   if (transaction.type === TransactionType.Refund) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -786,7 +776,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Refund`}
-          total={total}
+          total={effective}
           kind="positive"
         >
           {transaction.info.summary}
@@ -817,48 +807,17 @@ export function TransactionView({
         />
         <Part
           title={i18n.str`Details`}
-          text={<RefundDetails transaction={transaction} />}
+          text={
+            <RefundDetails
+              amount={getAmountWithFee(effective, raw, "credit")}
+            />
+          }
         />
       </TransactionTemplate>
     );
   }
 
-  function ShowQrWithCopy({ text }: { text: string }): VNode {
-    const [showing, setShowing] = useState(false);
-    async function copy(): Promise<void> {
-      navigator.clipboard.writeText(text);
-    }
-    async function toggle(): Promise<void> {
-      setShowing((s) => !s);
-    }
-    if (showing) {
-      return (
-        <div>
-          <QR text={text} />
-          <Button onClick={copy as SafeHandler<void>}>
-            <i18n.Translate>copy</i18n.Translate>
-          </Button>
-          <Button onClick={toggle as SafeHandler<void>}>
-            <i18n.Translate>hide qr</i18n.Translate>
-          </Button>
-        </div>
-      );
-    }
-    return (
-      <div>
-        <div>{text.substring(0, 64)}...</div>
-        <Button onClick={copy as SafeHandler<void>}>
-          <i18n.Translate>copy</i18n.Translate>
-        </Button>
-        <Button onClick={toggle as SafeHandler<void>}>
-          <i18n.Translate>show qr</i18n.Translate>
-        </Button>
-      </div>
-    );
-  }
-
   if (transaction.type === TransactionType.PeerPullCredit) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -870,7 +829,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Credit`}
-          total={total}
+          total={effective}
           kind="positive"
         >
           <i18n.Translate>Invoice</i18n.Translate>
@@ -900,10 +859,7 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <InvoiceDetails
-              amount={{
-                effective: Amounts.parseOrThrow(transaction.amountEffective),
-                raw: Amounts.parseOrThrow(transaction.amountRaw),
-              }}
+              amount={getAmountWithFee(effective, raw, "credit")}
             />
           }
         />
@@ -912,7 +868,6 @@ export function TransactionView({
   }
 
   if (transaction.type === TransactionType.PeerPullDebit) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -924,7 +879,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Debit`}
-          total={total}
+          total={effective}
           kind="negative"
         >
           <i18n.Translate>Invoice</i18n.Translate>
@@ -946,16 +901,14 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <InvoiceDetails
-              amount={{
-                effective: Amounts.parseOrThrow(transaction.amountEffective),
-                raw: Amounts.parseOrThrow(transaction.amountRaw),
-              }}
+              amount={getAmountWithFee(effective, raw, "debit")}
             />
           }
         />
       </TransactionTemplate>
     );
   }
+
   if (transaction.type === TransactionType.PeerPushDebit) {
     const total = Amounts.parseOrThrow(transaction.amountEffective);
     return (
@@ -998,10 +951,7 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <TransferDetails
-              amount={{
-                effective: Amounts.parseOrThrow(transaction.amountEffective),
-                raw: Amounts.parseOrThrow(transaction.amountRaw),
-              }}
+              amount={getAmountWithFee(effective, raw, "debit")}
             />
           }
         />
@@ -1010,7 +960,6 @@ export function TransactionView({
   }
 
   if (transaction.type === TransactionType.PeerPushCredit) {
-    const total = Amounts.parseOrThrow(transaction.amountEffective);
     return (
       <TransactionTemplate
         transaction={transaction}
@@ -1022,7 +971,7 @@ export function TransactionView({
         <Header
           timestamp={transaction.timestamp}
           type={i18n.str`Credit`}
-          total={total}
+          total={effective}
           kind="positive"
         >
           <i18n.Translate>Transfer</i18n.Translate>
@@ -1044,17 +993,14 @@ export function TransactionView({
           title={i18n.str`Details`}
           text={
             <TransferDetails
-              amount={{
-                effective: Amounts.parseOrThrow(transaction.amountEffective),
-                raw: Amounts.parseOrThrow(transaction.amountRaw),
-              }}
+              amount={getAmountWithFee(effective, raw, "credit")}
             />
           }
         />
       </TransactionTemplate>
     );
   }
-  return <div />;
+  assertUnreachable(transaction);
 }
 
 export function MerchantDetails({
@@ -1231,19 +1177,37 @@ export function ExchangeDetails({ exchange }: { 
exchange: string }): VNode {
 }
 
 export interface AmountWithFee {
-  effective: AmountJson;
-  raw: AmountJson;
+  value: AmountJson;
+  fee: AmountJson;
+  total: AmountJson;
+  maxFrac: number;
 }
 
-export function InvoiceDetails({ amount }: { amount: AmountWithFee }): VNode {
-  const { i18n } = useTranslationContext();
-
-  const fee = Amounts.sub(amount.raw, amount.effective).amount;
-
-  const maxFrac = [amount.raw, amount.effective, fee]
+export function getAmountWithFee(
+  effective: AmountJson,
+  raw: AmountJson,
+  direction: "credit" | "debit",
+): AmountWithFee {
+  const fee =
+    direction === "credit"
+      ? Amounts.sub(raw, effective).amount
+      : Amounts.sub(effective, raw).amount;
+
+  const maxFrac = [effective, raw, fee]
     .map((a) => Amounts.maxFractionalDigits(a))
     .reduce((c, p) => Math.max(c, p), 0);
 
+  return {
+    total: effective,
+    value: raw,
+    fee,
+    maxFrac,
+  };
+}
+
+export function InvoiceDetails({ amount }: { amount: AmountWithFee }): VNode {
+  const { i18n } = useTranslationContext();
+
   return (
     <PurchaseDetailsTable>
       <tr>
@@ -1251,17 +1215,17 @@ export function InvoiceDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Invoice</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.raw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} negative maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1275,7 +1239,7 @@ export function InvoiceDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.effective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
@@ -1285,12 +1249,6 @@ export function InvoiceDetails({ amount }: { amount: 
AmountWithFee }): VNode {
 export function TransferDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
 
-  const fee = Amounts.sub(amount.effective, amount.raw).amount;
-
-  const maxFrac = [amount.raw, amount.effective, fee]
-    .map((a) => Amounts.maxFractionalDigits(a))
-    .reduce((c, p) => Math.max(c, p), 0);
-
   return (
     <PurchaseDetailsTable>
       <tr>
@@ -1298,17 +1256,17 @@ export function TransferDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Transfer</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.raw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} negative maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1322,7 +1280,7 @@ export function TransferDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.effective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
@@ -1332,12 +1290,12 @@ export function TransferDetails({ amount }: { amount: 
AmountWithFee }): VNode {
 export function WithdrawDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
 
-  const fee = Amounts.sub(amount.raw, amount.effective).amount;
-
-  const maxFrac = [amount.raw, amount.effective, fee]
+  const maxFrac = [amount.fee, amount.fee]
     .map((a) => Amounts.maxFractionalDigits(a))
     .reduce((c, p) => Math.max(c, p), 0);
 
+  const total = Amounts.add(amount.value, amount.fee).amount;
+
   return (
     <PurchaseDetailsTable>
       <tr>
@@ -1345,17 +1303,17 @@ export function WithdrawDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Withdraw</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.raw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} negative maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1369,7 +1327,7 @@ export function WithdrawDetails({ amount }: { amount: 
AmountWithFee }): VNode {
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={amount.effective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
@@ -1378,24 +1336,18 @@ export function WithdrawDetails({ amount }: { amount: 
AmountWithFee }): VNode {
 
 export function PurchaseDetails({
   price,
-  refund,
+  effectiveRefund,
   info,
   proposalId,
 }: {
   price: AmountWithFee;
-  refund?: AmountWithFee;
+  effectiveRefund?: AmountJson;
   info: OrderShortInfo;
   proposalId: string;
 }): VNode {
   const { i18n } = useTranslationContext();
 
-  const partialFee = Amounts.sub(price.effective, price.raw).amount;
-
-  const refundFee = !refund
-    ? Amounts.zeroOfCurrency(price.effective.currency)
-    : Amounts.sub(refund.raw, refund.effective).amount;
-
-  const fee = Amounts.sum([partialFee, refundFee]).amount;
+  const total = Amounts.add(price.value, price.fee).amount;
 
   const hasProducts = info.products && info.products.length > 0;
 
@@ -1406,10 +1358,6 @@ export function PurchaseDetails({
     return;
   };
 
-  const total = !refund
-    ? price.effective
-    : Amounts.sub(price.effective, refund.effective).amount;
-
   return (
     <PurchaseDetailsTable>
       <tr>
@@ -1417,43 +1365,73 @@ export function PurchaseDetails({
           <i18n.Translate>Price</i18n.Translate>
         </td>
         <td>
-          <Amount value={price.raw} />
+          <Amount value={price.value} />
         </td>
       </tr>
-
-      {refund && Amounts.isNonZero(refund.raw) && (
-        <tr>
-          <td>
-            <i18n.Translate>Refunded</i18n.Translate>
-          </td>
-          <td>
-            <Amount value={refund.raw} negative />
-          </td>
-        </tr>
-      )}
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(price.fee) && (
         <tr>
           <td>
             <i18n.Translate>Transaction fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} />
+            <Amount value={price.fee} />
           </td>
         </tr>
       )}
-      <tr>
-        <td colSpan={2}>
-          <hr />
-        </td>
-      </tr>
-      <tr>
-        <td>
-          <i18n.Translate>Total</i18n.Translate>
-        </td>
-        <td>
-          <Amount value={total} />
-        </td>
-      </tr>
+      {effectiveRefund && Amounts.isNonZero(effectiveRefund) ? (
+        <Fragment>
+          <tr>
+            <td colSpan={2}>
+              <hr />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <i18n.Translate>Subtotal</i18n.Translate>
+            </td>
+            <td>
+              <Amount value={price.total} />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <i18n.Translate>Refunded</i18n.Translate>
+            </td>
+            <td>
+              <Amount value={effectiveRefund} negative />
+            </td>
+          </tr>
+          <tr>
+            <td colSpan={2}>
+              <hr />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <i18n.Translate>Total</i18n.Translate>
+            </td>
+            <td>
+              <Amount value={Amounts.sub(total, effectiveRefund).amount} />
+            </td>
+          </tr>
+        </Fragment>
+      ) : (
+        <Fragment>
+          <tr>
+            <td colSpan={2}>
+              <hr />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <i18n.Translate>Total</i18n.Translate>
+            </td>
+            <td>
+              <Amount value={price.total} />
+            </td>
+          </tr>
+        </Fragment>
+      )}
       {hasProducts && (
         <tr>
           <td colSpan={2}>
@@ -1508,39 +1486,27 @@ export function PurchaseDetails({
   );
 }
 
-function RefundDetails({
-  transaction,
-}: {
-  transaction: TransactionRefund;
-}): VNode {
+function RefundDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
 
-  const r = Amounts.parseOrThrow(transaction.amountRaw);
-  const e = Amounts.parseOrThrow(transaction.amountEffective);
-  const fee = Amounts.sub(r, e).amount;
-
-  const maxFrac = [r, e, fee]
-    .map((a) => Amounts.maxFractionalDigits(a))
-    .reduce((c, p) => Math.max(c, p), 0);
-
   return (
     <PurchaseDetailsTable>
       <tr>
         <td>
-          <i18n.Translate>Amount</i18n.Translate>
+          <i18n.Translate>Refund</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountRaw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} negative maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1554,45 +1520,34 @@ function RefundDetails({
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountEffective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
   );
 }
 
-function DepositDetails({
-  transaction,
-}: {
-  transaction: TransactionDeposit;
-}): VNode {
+function DepositDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
-  const r = Amounts.parseOrThrow(transaction.amountRaw);
-  const e = Amounts.parseOrThrow(transaction.amountEffective);
-  const fee = Amounts.sub(e, r).amount;
-
-  const maxFrac = [r, e, fee]
-    .map((a) => Amounts.maxFractionalDigits(a))
-    .reduce((c, p) => Math.max(c, p), 0);
 
   return (
     <PurchaseDetailsTable>
       <tr>
         <td>
-          <i18n.Translate>Amount</i18n.Translate>
+          <i18n.Translate>Deposit</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountRaw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1606,43 +1561,32 @@ function DepositDetails({
           <i18n.Translate>Total transfer</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountEffective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
   );
 }
-function RefreshDetails({
-  transaction,
-}: {
-  transaction: TransactionRefresh;
-}): VNode {
-  const { i18n } = useTranslationContext();
-
-  const r = Amounts.parseOrThrow(transaction.amountRaw);
-  const e = Amounts.parseOrThrow(transaction.amountEffective);
-  const fee = Amounts.sub(r, e).amount;
 
-  const maxFrac = [r, e, fee]
-    .map((a) => Amounts.maxFractionalDigits(a))
-    .reduce((c, p) => Math.max(c, p), 0);
+function RefreshDetails({ amount }: { amount: AmountWithFee }): VNode {
+  const { i18n } = useTranslationContext();
 
   return (
     <PurchaseDetailsTable>
       <tr>
         <td>
-          <i18n.Translate>Amount</i18n.Translate>
+          <i18n.Translate>Refresh</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountRaw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
       <tr>
         <td>
-          <i18n.Translate>Transaction fees</i18n.Translate>
+          <i18n.Translate>Fees</i18n.Translate>
         </td>
         <td>
-          <Amount value={fee} negative maxFracSize={maxFrac} />
+          <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
       <tr>
@@ -1655,42 +1599,34 @@ function RefreshDetails({
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountEffective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
   );
 }
 
-function TipDetails({ transaction }: { transaction: TransactionTip }): VNode {
+function TipDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
 
-  const r = Amounts.parseOrThrow(transaction.amountRaw);
-  const e = Amounts.parseOrThrow(transaction.amountEffective);
-  const fee = Amounts.sub(r, e).amount;
-
-  const maxFrac = [r, e, fee]
-    .map((a) => Amounts.maxFractionalDigits(a))
-    .reduce((c, p) => Math.max(c, p), 0);
-
   return (
     <PurchaseDetailsTable>
       <tr>
         <td>
-          <i18n.Translate>Amount</i18n.Translate>
+          <i18n.Translate>Tip</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountRaw} maxFracSize={maxFrac} />
+          <Amount value={amount.value} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
 
-      {Amounts.isNonZero(fee) && (
+      {Amounts.isNonZero(amount.fee) && (
         <tr>
           <td>
-            <i18n.Translate>Transaction fees</i18n.Translate>
+            <i18n.Translate>Fees</i18n.Translate>
           </td>
           <td>
-            <Amount value={fee} negative maxFracSize={maxFrac} />
+            <Amount value={amount.fee} maxFracSize={amount.maxFrac} />
           </td>
         </tr>
       )}
@@ -1704,7 +1640,7 @@ function TipDetails({ transaction }: { transaction: 
TransactionTip }): VNode {
           <i18n.Translate>Total</i18n.Translate>
         </td>
         <td>
-          <Amount value={transaction.amountEffective} maxFracSize={maxFrac} />
+          <Amount value={amount.total} maxFracSize={amount.maxFrac} />
         </td>
       </tr>
     </PurchaseDetailsTable>
@@ -1778,3 +1714,38 @@ function NicePayto({ payto }: { payto: PaytoUri }): 
VNode {
   }
   return <Fragment>{stringifyPaytoUri(payto)}</Fragment>;
 }
+
+function ShowQrWithCopy({ text }: { text: string }): VNode {
+  const [showing, setShowing] = useState(false);
+  const { i18n } = useTranslationContext();
+  async function copy(): Promise<void> {
+    navigator.clipboard.writeText(text);
+  }
+  async function toggle(): Promise<void> {
+    setShowing((s) => !s);
+  }
+  if (showing) {
+    return (
+      <div>
+        <QR text={text} />
+        <Button onClick={copy as SafeHandler<void>}>
+          <i18n.Translate>copy</i18n.Translate>
+        </Button>
+        <Button onClick={toggle as SafeHandler<void>}>
+          <i18n.Translate>hide qr</i18n.Translate>
+        </Button>
+      </div>
+    );
+  }
+  return (
+    <div>
+      <div>{text.substring(0, 64)}...</div>
+      <Button onClick={copy as SafeHandler<void>}>
+        <i18n.Translate>copy</i18n.Translate>
+      </Button>
+      <Button onClick={toggle as SafeHandler<void>}>
+        <i18n.Translate>show qr</i18n.Translate>
+      </Button>
+    </div>
+  );
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]