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