aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-01-15 17:49:57 -0300
committerSebastian <sebasjm@gmail.com>2023-01-15 17:49:57 -0300
commitbd57fa46a44db8e8c685b40f66eaa7998e71efa5 (patch)
tree35dbf2c3125c744bea500d8b73e35a929179b7ab /packages/taler-wallet-webextension
parentfc38d0da958323b994d2e4f8a8f2e9632865557f (diff)
show deposit transaction info
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts222
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts23
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx52
3 files changed, 164 insertions, 133 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index 1b628047a..d7d9f2da7 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -29,13 +29,14 @@ import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "../../context/translation.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
+import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";
export function useComponentState({
amount: amountStr,
onCancel,
onSuccess,
-}: Props): State {
+}: Props): RecursiveState<State> {
const api = useBackendContext();
const { i18n } = useTranslationContext();
const { pushAlertOnError } = useAlertContext();
@@ -49,9 +50,7 @@ export function useComponentState({
);
const { accounts } = await api.wallet.call(
WalletApiOperation.ListKnownBankAccounts,
- {
- currency,
- },
+ { currency },
);
return { accounts, balances };
@@ -61,13 +60,11 @@ export function useComponentState({
parsed !== undefined
? parsed
: currency !== undefined
- ? Amounts.zeroOfCurrency(currency)
- : undefined;
+ ? Amounts.zeroOfCurrency(currency)
+ : undefined;
// const [accountIdx, setAccountIdx] = useState<number>(0);
- const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as any));
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
- const [fee, setFee] = useState<DepositGroupFees | undefined>(undefined);
const [addingAccount, setAddingAccount] = useState(false);
if (!currency) {
@@ -91,7 +88,12 @@ export function useComponentState({
}
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) {
return {
@@ -139,128 +141,110 @@ export function useComponentState({
const firstAccount = accounts[0].uri;
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
- if (fee === undefined) {
- getFeeForAmount(currentAccount, amount, api.wallet).then((initialFee) => {
- setFee(initialFee);
- });
- return {
- status: "loading",
- error: undefined,
- };
- }
+ return () => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const [amount, setAmount] = useState<AmountJson>(
+ initialValue ?? ({} as any),
+ );
+ const amountStr = Amounts.stringify(amount);
+ const depositPaytoUri = `payto://${currentAccount.targetType}/${currentAccount.targetPath}`;
- const accountMap = createLabelsForBankAccount(accounts);
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const hook = useAsyncAsHook(async () => {
+ const fee = await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
+ amount: amountStr,
+ depositPaytoUri,
+ });
- 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);
- }
- }
- }
+ return { fee };
+ }, [amountStr, depositPaytoUri]);
- 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);
+ if (!hook) {
+ return {
+ status: "loading",
+ error: undefined,
+ };
+ }
+ if (hook.hasError) {
+ return {
+ status: "error",
+ error: alertFromError(
+ i18n.str`Could not load fee for amount ${amountStr}`,
+ hook,
+ ),
+ };
}
- }
- const totalFee =
- fee !== undefined
- ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
- : Amounts.zeroOfCurrency(currency);
+ const { fee } = hook.response;
- const totalToDeposit =
- fee !== undefined
- ? Amounts.sub(amount, totalFee).amount
- : Amounts.zeroOfCurrency(currency);
+ const accountMap = createLabelsForBankAccount(accounts);
- const isDirty = amount !== initialValue;
- const amountError = !isDirty
- ? undefined
- : Amounts.cmp(balance, amount) === -1
- ? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
- : undefined;
+ const totalFee =
+ fee !== undefined
+ ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
+ : Amounts.zeroOfCurrency(currency);
- 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
+ const totalToDeposit =
+ fee !== undefined
+ ? Amounts.sub(amount, totalFee).amount
+ : Amounts.zeroOfCurrency(currency);
- async function doSend(): Promise<void> {
- if (!currency) return;
+ const isDirty = amount !== initialValue;
+ const amountError = !isDirty
+ ? undefined
+ : Amounts.cmp(balance, amount) === -1
+ ? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
+ : undefined;
- const depositPaytoUri = stringifyPaytoUri(currentAccount);
- const amountStr = Amounts.stringify(amount);
- await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
- amount: amountStr,
- depositPaytoUri,
- });
- onSuccess(currency);
- }
+ 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
- return {
- status: "ready",
- error: undefined,
- currency,
- amount: {
- value: amount,
- onInput: pushAlertOnError(updateAmount),
- 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,
- // currentAccount,
- // parsedAmount,
- };
-}
+ async function doSend(): Promise<void> {
+ if (!currency) return;
-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,
- });
+ 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,
+ };
+ };
}
export function labelForAccountType(id: string): string {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 42b76cf50..bfd69f945 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -264,6 +264,15 @@ describe("DepositPage states", () => {
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
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,
);
@@ -341,7 +350,7 @@ describe("DepositPage states", () => {
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
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;
if (!state.amount.onInput) return;
@@ -359,6 +368,18 @@ describe("DepositPage states", () => {
);
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,
);
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 5ed05f87f..cc3a65f2d 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -38,7 +38,7 @@ import {
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
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 { useEffect, useState } from "preact/hooks";
import emptyImg from "../../static/img/empty.png";
@@ -641,6 +641,11 @@ export function TransactionView({
if (transaction.type === TransactionType.Deposit) {
const total = Amounts.parseOrThrow(transaction.amountRaw);
const payto = parsePaytoUri(transaction.targetPaytoUri);
+
+ const wireTime = AbsoluteTime.fromTimestamp(
+ transaction.wireTransferDeadline,
+ );
+ const shouldBeWired = wireTime.t_ms !== "never" && isPast(wireTime.t_ms);
return (
<TransactionTemplate
transaction={transaction}
@@ -663,18 +668,39 @@ export function TransactionView({
text={<DepositDetails transaction={transaction} />}
kind="neutral"
/>
- <Part
- title={i18n.str`Wire transfer deadline`}
- text={
- <Time
- timestamp={AbsoluteTime.fromTimestamp(
- transaction.wireTransferDeadline,
- )}
- format="dd MMMM yyyy 'at' HH:mm"
- />
- }
- kind="neutral"
- />
+ {!shouldBeWired ? (
+ <Part
+ title={i18n.str`Wire transfer deadline`}
+ text={
+ <Time timestamp={wireTime} format="dd MMMM yyyy 'at' HH:mm" />
+ }
+ kind="neutral"
+ />
+ ) : transaction.wireTransferProgress === 0 ? (
+ <AlertView
+ 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>
);
}