aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src')
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx12
-rw-r--r--packages/taler-wallet-webextension/src/cta/Payment/views.tsx19
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx17
-rw-r--r--packages/taler-wallet-webextension/src/platform/chrome.ts34
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx453
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>
+ );
+}