show deposit transaction info
This commit is contained in:
parent
fc38d0da95
commit
bd57fa46a4
@ -29,13 +29,14 @@ import { alertFromError, useAlertContext } from "../../context/alert.js";
|
|||||||
import { useBackendContext } from "../../context/backend.js";
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useTranslationContext } from "../../context/translation.js";
|
import { useTranslationContext } from "../../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
|
import { RecursiveState } from "../../utils/index.js";
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState({
|
export function useComponentState({
|
||||||
amount: amountStr,
|
amount: amountStr,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}: Props): State {
|
}: Props): RecursiveState<State> {
|
||||||
const api = useBackendContext();
|
const api = useBackendContext();
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const { pushAlertOnError } = useAlertContext();
|
const { pushAlertOnError } = useAlertContext();
|
||||||
@ -49,9 +50,7 @@ export function useComponentState({
|
|||||||
);
|
);
|
||||||
const { accounts } = await api.wallet.call(
|
const { accounts } = await api.wallet.call(
|
||||||
WalletApiOperation.ListKnownBankAccounts,
|
WalletApiOperation.ListKnownBankAccounts,
|
||||||
{
|
{ currency },
|
||||||
currency,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return { accounts, balances };
|
return { accounts, balances };
|
||||||
@ -61,13 +60,11 @@ export function useComponentState({
|
|||||||
parsed !== undefined
|
parsed !== undefined
|
||||||
? parsed
|
? parsed
|
||||||
: currency !== undefined
|
: currency !== undefined
|
||||||
? Amounts.zeroOfCurrency(currency)
|
? Amounts.zeroOfCurrency(currency)
|
||||||
: undefined;
|
: undefined;
|
||||||
// const [accountIdx, setAccountIdx] = useState<number>(0);
|
// const [accountIdx, setAccountIdx] = useState<number>(0);
|
||||||
const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as any));
|
|
||||||
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
|
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
|
||||||
|
|
||||||
const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
|
|
||||||
const [addingAccount, setAddingAccount] = useState(false);
|
const [addingAccount, setAddingAccount] = useState(false);
|
||||||
|
|
||||||
if (!currency) {
|
if (!currency) {
|
||||||
@ -91,7 +88,12 @@ export function useComponentState({
|
|||||||
}
|
}
|
||||||
const { accounts, balances } = hook.response;
|
const { accounts, balances } = hook.response;
|
||||||
|
|
||||||
// const parsedAmount = Amounts.parse(`${currency}:${amount}`);
|
async function updateAccountFromList(accountStr: string): Promise<void> {
|
||||||
|
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
|
||||||
|
if (uri) {
|
||||||
|
setSelectedAccount(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addingAccount) {
|
if (addingAccount) {
|
||||||
return {
|
return {
|
||||||
@ -139,130 +141,112 @@ export function useComponentState({
|
|||||||
const firstAccount = accounts[0].uri;
|
const firstAccount = accounts[0].uri;
|
||||||
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
|
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
|
||||||
|
|
||||||
if (fee === undefined) {
|
return () => {
|
||||||
getFeeForAmount(currentAccount, amount, api.wallet).then((initialFee) => {
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
setFee(initialFee);
|
const [amount, setAmount] = useState<AmountJson>(
|
||||||
});
|
initialValue ?? ({} as any),
|
||||||
return {
|
);
|
||||||
status: "loading",
|
|
||||||
error: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountMap = createLabelsForBankAccount(accounts);
|
|
||||||
|
|
||||||
async function updateAccountFromList(accountStr: string): Promise<void> {
|
|
||||||
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
|
|
||||||
if (uri) {
|
|
||||||
try {
|
|
||||||
const result = await getFeeForAmount(uri, amount, api.wallet);
|
|
||||||
setSelectedAccount(uri);
|
|
||||||
setFee(result);
|
|
||||||
} catch (e) {
|
|
||||||
setSelectedAccount(uri);
|
|
||||||
setFee(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateAmount(newAmount: AmountJson): Promise<void> {
|
|
||||||
// const parsed = Amounts.parse(`${currency}:${numStr}`);
|
|
||||||
try {
|
|
||||||
const result = await getFeeForAmount(
|
|
||||||
currentAccount,
|
|
||||||
newAmount,
|
|
||||||
api.wallet,
|
|
||||||
);
|
|
||||||
setAmount(newAmount);
|
|
||||||
setFee(result);
|
|
||||||
} catch (e) {
|
|
||||||
setAmount(newAmount);
|
|
||||||
setFee(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalFee =
|
|
||||||
fee !== undefined
|
|
||||||
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
|
|
||||||
: Amounts.zeroOfCurrency(currency);
|
|
||||||
|
|
||||||
const totalToDeposit =
|
|
||||||
fee !== undefined
|
|
||||||
? Amounts.sub(amount, totalFee).amount
|
|
||||||
: Amounts.zeroOfCurrency(currency);
|
|
||||||
|
|
||||||
const isDirty = amount !== initialValue;
|
|
||||||
const amountError = !isDirty
|
|
||||||
? undefined
|
|
||||||
: Amounts.cmp(balance, amount) === -1
|
|
||||||
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const unableToDeposit =
|
|
||||||
Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
|
|
||||||
fee === undefined || //no fee calculated yet
|
|
||||||
amountError !== undefined; //amount field may be invalid
|
|
||||||
|
|
||||||
async function doSend(): Promise<void> {
|
|
||||||
if (!currency) return;
|
|
||||||
|
|
||||||
const depositPaytoUri = stringifyPaytoUri(currentAccount);
|
|
||||||
const amountStr = Amounts.stringify(amount);
|
const amountStr = Amounts.stringify(amount);
|
||||||
await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
|
const depositPaytoUri = `payto://${currentAccount.targetType}/${currentAccount.targetPath}`;
|
||||||
amount: amountStr,
|
|
||||||
depositPaytoUri,
|
|
||||||
});
|
|
||||||
onSuccess(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
status: "ready",
|
const hook = useAsyncAsHook(async () => {
|
||||||
error: undefined,
|
const fee = await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
|
||||||
currency,
|
amount: amountStr,
|
||||||
amount: {
|
depositPaytoUri,
|
||||||
value: amount,
|
});
|
||||||
onInput: pushAlertOnError(updateAmount),
|
|
||||||
error: amountError,
|
return { fee };
|
||||||
},
|
}, [amountStr, depositPaytoUri]);
|
||||||
onAddAccount: {
|
|
||||||
onClick: pushAlertOnError(async () => {
|
if (!hook) {
|
||||||
setAddingAccount(true);
|
return {
|
||||||
}),
|
status: "loading",
|
||||||
},
|
error: undefined,
|
||||||
account: {
|
};
|
||||||
list: accountMap,
|
}
|
||||||
value: stringifyPaytoUri(currentAccount),
|
if (hook.hasError) {
|
||||||
onChange: pushAlertOnError(updateAccountFromList),
|
return {
|
||||||
},
|
status: "error",
|
||||||
currentAccount,
|
error: alertFromError(
|
||||||
cancelHandler: {
|
i18n.str`Could not load fee for amount ${amountStr}`,
|
||||||
onClick: pushAlertOnError(async () => {
|
hook,
|
||||||
onCancel(currency);
|
),
|
||||||
}),
|
};
|
||||||
},
|
}
|
||||||
depositHandler: {
|
|
||||||
onClick: unableToDeposit ? undefined : pushAlertOnError(doSend),
|
const { fee } = hook.response;
|
||||||
},
|
|
||||||
totalFee,
|
const accountMap = createLabelsForBankAccount(accounts);
|
||||||
totalToDeposit,
|
|
||||||
// currentAccount,
|
const totalFee =
|
||||||
// parsedAmount,
|
fee !== undefined
|
||||||
|
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
|
||||||
|
: Amounts.zeroOfCurrency(currency);
|
||||||
|
|
||||||
|
const totalToDeposit =
|
||||||
|
fee !== undefined
|
||||||
|
? Amounts.sub(amount, totalFee).amount
|
||||||
|
: Amounts.zeroOfCurrency(currency);
|
||||||
|
|
||||||
|
const isDirty = amount !== initialValue;
|
||||||
|
const amountError = !isDirty
|
||||||
|
? undefined
|
||||||
|
: Amounts.cmp(balance, amount) === -1
|
||||||
|
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const unableToDeposit =
|
||||||
|
Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
|
||||||
|
fee === undefined || //no fee calculated yet
|
||||||
|
amountError !== undefined; //amount field may be invalid
|
||||||
|
|
||||||
|
async function doSend(): Promise<void> {
|
||||||
|
if (!currency) return;
|
||||||
|
|
||||||
|
const depositPaytoUri = stringifyPaytoUri(currentAccount);
|
||||||
|
const amountStr = Amounts.stringify(amount);
|
||||||
|
await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
|
||||||
|
amount: amountStr,
|
||||||
|
depositPaytoUri,
|
||||||
|
});
|
||||||
|
onSuccess(currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: "ready",
|
||||||
|
error: undefined,
|
||||||
|
currency,
|
||||||
|
amount: {
|
||||||
|
value: amount,
|
||||||
|
onInput: pushAlertOnError(async (a) => setAmount(a)),
|
||||||
|
error: amountError,
|
||||||
|
},
|
||||||
|
onAddAccount: {
|
||||||
|
onClick: pushAlertOnError(async () => {
|
||||||
|
setAddingAccount(true);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
account: {
|
||||||
|
list: accountMap,
|
||||||
|
value: stringifyPaytoUri(currentAccount),
|
||||||
|
onChange: pushAlertOnError(updateAccountFromList),
|
||||||
|
},
|
||||||
|
currentAccount,
|
||||||
|
cancelHandler: {
|
||||||
|
onClick: pushAlertOnError(async () => {
|
||||||
|
onCancel(currency);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
depositHandler: {
|
||||||
|
onClick: unableToDeposit ? undefined : pushAlertOnError(doSend),
|
||||||
|
},
|
||||||
|
totalFee,
|
||||||
|
totalToDeposit,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFeeForAmount(
|
|
||||||
p: PaytoUri,
|
|
||||||
a: AmountJson,
|
|
||||||
wallet: ReturnType<typeof useBackendContext>["wallet"],
|
|
||||||
): Promise<DepositGroupFees> {
|
|
||||||
const depositPaytoUri = `payto://${p.targetType}/${p.targetPath}`;
|
|
||||||
const amount = Amounts.stringify(a);
|
|
||||||
return await wallet.call(WalletApiOperation.GetFeeForDeposit, {
|
|
||||||
amount,
|
|
||||||
depositPaytoUri,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function labelForAccountType(id: string): string {
|
export function labelForAccountType(id: string): string {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case "":
|
case "":
|
||||||
|
@ -264,6 +264,15 @@ describe("DepositPage states", () => {
|
|||||||
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
expect(state.depositHandler.onClick).undefined;
|
expect(state.depositHandler.onClick).undefined;
|
||||||
},
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(accountSelected);
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
},
|
||||||
],
|
],
|
||||||
TestingContext,
|
TestingContext,
|
||||||
);
|
);
|
||||||
@ -341,7 +350,7 @@ describe("DepositPage states", () => {
|
|||||||
expect(state.account.value).eq(accountSelected);
|
expect(state.account.value).eq(accountSelected);
|
||||||
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
expect(state.depositHandler.onClick).undefined;
|
expect(state.depositHandler.onClick).undefined;
|
||||||
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
|
|
||||||
expect(state.amount.onInput).not.undefined;
|
expect(state.amount.onInput).not.undefined;
|
||||||
if (!state.amount.onInput) return;
|
if (!state.amount.onInput) return;
|
||||||
@ -359,6 +368,18 @@ describe("DepositPage states", () => {
|
|||||||
);
|
);
|
||||||
expect(state.depositHandler.onClick).not.undefined;
|
expect(state.depositHandler.onClick).not.undefined;
|
||||||
},
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(accountSelected);
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
||||||
|
expect(state.totalToDeposit).deep.eq(
|
||||||
|
Amounts.parseOrThrow(`${currency}:7`),
|
||||||
|
);
|
||||||
|
expect(state.depositHandler.onClick).not.undefined;
|
||||||
|
},
|
||||||
],
|
],
|
||||||
TestingContext,
|
TestingContext,
|
||||||
);
|
);
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
} 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 } from "date-fns";
|
import { differenceInSeconds, isAfter, isFuture, 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";
|
||||||
@ -641,6 +641,11 @@ export function TransactionView({
|
|||||||
if (transaction.type === TransactionType.Deposit) {
|
if (transaction.type === TransactionType.Deposit) {
|
||||||
const total = Amounts.parseOrThrow(transaction.amountRaw);
|
const total = Amounts.parseOrThrow(transaction.amountRaw);
|
||||||
const payto = parsePaytoUri(transaction.targetPaytoUri);
|
const payto = parsePaytoUri(transaction.targetPaytoUri);
|
||||||
|
|
||||||
|
const wireTime = AbsoluteTime.fromTimestamp(
|
||||||
|
transaction.wireTransferDeadline,
|
||||||
|
);
|
||||||
|
const shouldBeWired = wireTime.t_ms !== "never" && isPast(wireTime.t_ms);
|
||||||
return (
|
return (
|
||||||
<TransactionTemplate
|
<TransactionTemplate
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
@ -663,18 +668,39 @@ export function TransactionView({
|
|||||||
text={<DepositDetails transaction={transaction} />}
|
text={<DepositDetails transaction={transaction} />}
|
||||||
kind="neutral"
|
kind="neutral"
|
||||||
/>
|
/>
|
||||||
<Part
|
{!shouldBeWired ? (
|
||||||
title={i18n.str`Wire transfer deadline`}
|
<Part
|
||||||
text={
|
title={i18n.str`Wire transfer deadline`}
|
||||||
<Time
|
text={
|
||||||
timestamp={AbsoluteTime.fromTimestamp(
|
<Time timestamp={wireTime} format="dd MMMM yyyy 'at' HH:mm" />
|
||||||
transaction.wireTransferDeadline,
|
}
|
||||||
)}
|
kind="neutral"
|
||||||
format="dd MMMM yyyy 'at' HH:mm"
|
/>
|
||||||
/>
|
) : transaction.wireTransferProgress === 0 ? (
|
||||||
}
|
<AlertView
|
||||||
kind="neutral"
|
alert={{
|
||||||
/>
|
type: "warning",
|
||||||
|
message: i18n.str`Wire transfer is not initiated`,
|
||||||
|
description: i18n.str` `,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : transaction.wireTransferProgress === 100 ? (
|
||||||
|
<AlertView
|
||||||
|
alert={{
|
||||||
|
type: "success",
|
||||||
|
message: i18n.str`Wire transfer completed`,
|
||||||
|
description: i18n.str` `,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AlertView
|
||||||
|
alert={{
|
||||||
|
type: "info",
|
||||||
|
message: i18n.str`Wire transfer in progress`,
|
||||||
|
description: i18n.str` `,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TransactionTemplate>
|
</TransactionTemplate>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user