pretty
This commit is contained in:
parent
860f10e6f0
commit
6ddb2de842
@ -23,11 +23,9 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { CompletedView, LoadingUriView, ReadyView } from "./views.js";
|
||||
|
||||
|
||||
|
||||
export interface Props {
|
||||
talerDepositUri: string | undefined,
|
||||
amountStr: AmountString | undefined,
|
||||
talerDepositUri: string | undefined;
|
||||
amountStr: AmountString | undefined;
|
||||
cancel: () => Promise<void>;
|
||||
}
|
||||
|
||||
@ -38,7 +36,6 @@ export type State =
|
||||
| State.Completed;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -63,10 +60,14 @@ export namespace State {
|
||||
}
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
"loading": Loading,
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
completed: CompletedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
export const DepositPage = compose("Deposit", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
export const DepositPage = compose(
|
||||
"Deposit",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,6 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { Amounts, CreateDepositGroupResponse } from "@gnu-taler/taler-util";
|
||||
import { useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -41,7 +40,7 @@ export function useComponentState(
|
||||
return { deposit, uri: talerDepositUri, amount };
|
||||
});
|
||||
|
||||
if (!info) return { status: "loading", error: undefined }
|
||||
if (!info) return { status: "loading", error: undefined };
|
||||
if (info.hasError) {
|
||||
return {
|
||||
status: "loading-uri",
|
||||
@ -74,4 +73,4 @@ export function useComponentState(
|
||||
effective: deposit.effectiveDepositAmount,
|
||||
cancel,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,7 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import {
|
||||
Amounts, PrepareDepositResponse
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Amounts, PrepareDepositResponse } from "@gnu-taler/taler-util";
|
||||
import { expect } from "chai";
|
||||
import { mountHook } from "../../test-utils.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
@ -30,11 +28,20 @@ describe("Deposit CTA states", () => {
|
||||
it("should tell the user that the URI is missing", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerDepositUri: undefined, amountStr: undefined, cancel: async () => { null } }, {
|
||||
prepareRefund: async () => ({}),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerDepositUri: undefined,
|
||||
amountStr: undefined,
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareRefund: async () => ({}),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -61,14 +68,23 @@ describe("Deposit CTA states", () => {
|
||||
it("should be ready after loading", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerDepositUri: "payto://refund/asdasdas", amountStr: "EUR:1", cancel: async () => { null } }, {
|
||||
prepareDeposit: async () =>
|
||||
({
|
||||
effectiveDepositAmount: Amounts.parseOrThrow("EUR:1"),
|
||||
totalDepositCost: Amounts.parseOrThrow("EUR:1.2"),
|
||||
} as PrepareDepositResponse as any),
|
||||
createDepositGroup: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerDepositUri: "payto://refund/asdasdas",
|
||||
amountStr: "EUR:1",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareDeposit: async () =>
|
||||
({
|
||||
effectiveDepositAmount: Amounts.parseOrThrow("EUR:1"),
|
||||
totalDepositCost: Amounts.parseOrThrow("EUR:1.2"),
|
||||
} as PrepareDepositResponse as any),
|
||||
createDepositGroup: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
|
@ -35,7 +35,6 @@ export type State =
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -59,8 +58,8 @@ export namespace State {
|
||||
status: "ready";
|
||||
create: ButtonHandler;
|
||||
subject: TextFieldHandler;
|
||||
toBeReceived: AmountJson,
|
||||
chosenAmount: AmountJson,
|
||||
toBeReceived: AmountJson;
|
||||
chosenAmount: AmountJson;
|
||||
exchangeUrl: string;
|
||||
invalid: boolean;
|
||||
error: undefined;
|
||||
@ -71,10 +70,12 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"created": CreatedView,
|
||||
"ready": ReadyView,
|
||||
created: CreatedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
||||
export const InvoiceCreatePage = compose("InvoiceCreatePage", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
|
||||
export const InvoiceCreatePage = compose(
|
||||
"InvoiceCreatePage",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -25,21 +25,22 @@ export function useComponentState(
|
||||
{ amount: amountStr, onClose }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const amount = Amounts.parseOrThrow(amountStr)
|
||||
const amount = Amounts.parseOrThrow(amountStr);
|
||||
|
||||
const [subject, setSubject] = useState("");
|
||||
const [talerUri, setTalerUri] = useState("")
|
||||
const [talerUri, setTalerUri] = useState("");
|
||||
|
||||
const hook = useAsyncAsHook(api.listExchanges);
|
||||
const [exchangeIdx, setExchangeIdx] = useState("0")
|
||||
const [operationError, setOperationError] = useState<TalerErrorDetail | undefined>(undefined)
|
||||
|
||||
const [exchangeIdx, setExchangeIdx] = useState("0");
|
||||
const [operationError, setOperationError] = useState<
|
||||
TalerErrorDetail | undefined
|
||||
>(undefined);
|
||||
|
||||
if (!hook) {
|
||||
return {
|
||||
status: "loading",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hook.hasError) {
|
||||
return {
|
||||
@ -54,62 +55,65 @@ export function useComponentState(
|
||||
talerUri,
|
||||
error: undefined,
|
||||
cancel: {
|
||||
onClick: onClose
|
||||
onClick: onClose,
|
||||
},
|
||||
copyToClipboard: {
|
||||
onClick: async () => {
|
||||
navigator.clipboard.writeText(talerUri);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const exchanges = hook.response.exchanges.filter(e => e.currency === amount.currency);
|
||||
const exchangeMap = exchanges.reduce((prev, cur, idx) => ({ ...prev, [String(idx)]: cur.exchangeBaseUrl }), {} as Record<string, string>)
|
||||
const exchanges = hook.response.exchanges.filter(
|
||||
(e) => e.currency === amount.currency,
|
||||
);
|
||||
const exchangeMap = exchanges.reduce(
|
||||
(prev, cur, idx) => ({ ...prev, [String(idx)]: cur.exchangeBaseUrl }),
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
const selected = exchanges[Number(exchangeIdx)];
|
||||
|
||||
async function accept(): Promise<string> {
|
||||
try {
|
||||
|
||||
const resp = await api.initiatePeerPullPayment({
|
||||
amount: Amounts.stringify(amount),
|
||||
exchangeBaseUrl: selected.exchangeBaseUrl,
|
||||
partialContractTerms: {
|
||||
summary: subject
|
||||
}
|
||||
})
|
||||
return resp.talerUri
|
||||
summary: subject,
|
||||
},
|
||||
});
|
||||
return resp.talerUri;
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail)
|
||||
setOperationError(e.errorDetail);
|
||||
}
|
||||
console.error(e)
|
||||
throw Error("error trying to accept")
|
||||
console.error(e);
|
||||
throw Error("error trying to accept");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
status: "ready",
|
||||
subject: {
|
||||
error: !subject ? "cant be empty" : undefined,
|
||||
value: subject,
|
||||
onInput: async (e) => setSubject(e)
|
||||
onInput: async (e) => setSubject(e),
|
||||
},
|
||||
invalid: !subject || Amounts.isZero(amount),
|
||||
exchangeUrl: selected.exchangeBaseUrl,
|
||||
create: {
|
||||
onClick: async () => {
|
||||
const uri = await accept();
|
||||
setTalerUri(uri)
|
||||
}
|
||||
setTalerUri(uri);
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
onClick: onClose
|
||||
onClick: onClose,
|
||||
},
|
||||
chosenAmount: amount,
|
||||
toBeReceived: amount,
|
||||
error: undefined,
|
||||
operationError
|
||||
}
|
||||
operationError,
|
||||
};
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("test description", () => {
|
||||
|
||||
it("should assert", () => {
|
||||
|
||||
expect([]).deep.equals([])
|
||||
expect([]).deep.equals([]);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -14,7 +14,12 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AbsoluteTime, AmountJson, PreparePayResult, TalerErrorDetail } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AbsoluteTime,
|
||||
AmountJson,
|
||||
PreparePayResult,
|
||||
TalerErrorDetail,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { ButtonHandler } from "../../mui/handlers.js";
|
||||
@ -37,7 +42,6 @@ export type State =
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -52,20 +56,20 @@ export namespace State {
|
||||
error: undefined;
|
||||
uri: string;
|
||||
cancel: ButtonHandler;
|
||||
amount: AmountJson,
|
||||
amount: AmountJson;
|
||||
goToWalletManualWithdraw: (currency: string) => Promise<void>;
|
||||
summary: string | undefined,
|
||||
expiration: AbsoluteTime | undefined,
|
||||
summary: string | undefined;
|
||||
expiration: AbsoluteTime | undefined;
|
||||
operationError?: TalerErrorDetail;
|
||||
payStatus: PreparePayResult;
|
||||
}
|
||||
|
||||
export interface NoBalanceForCurrency extends BaseInfo {
|
||||
status: "no-balance-for-currency"
|
||||
status: "no-balance-for-currency";
|
||||
balance: undefined;
|
||||
}
|
||||
export interface NoEnoughBalance extends BaseInfo {
|
||||
status: "no-enough-balance"
|
||||
status: "no-enough-balance";
|
||||
balance: AmountJson;
|
||||
}
|
||||
|
||||
@ -82,9 +86,11 @@ const viewMapping: StateViewMap<State> = {
|
||||
"loading-uri": LoadingUriView,
|
||||
"no-balance-for-currency": ReadyView,
|
||||
"no-enough-balance": ReadyView,
|
||||
"ready": ReadyView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
||||
export const InvoicePayPage = compose("InvoicePayPage", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
|
||||
export const InvoicePayPage = compose(
|
||||
"InvoicePayPage",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,15 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AbsoluteTime, Amounts, NotificationType, PreparePayResult, PreparePayResultType, TalerErrorDetail, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AbsoluteTime,
|
||||
Amounts,
|
||||
NotificationType,
|
||||
PreparePayResult,
|
||||
PreparePayResultType,
|
||||
TalerErrorDetail,
|
||||
TalerProtocolTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -27,11 +35,11 @@ export function useComponentState(
|
||||
): State {
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
const p2p = await api.checkPeerPullPayment({
|
||||
talerUri: talerPayPullUri
|
||||
})
|
||||
talerUri: talerPayPullUri,
|
||||
});
|
||||
const balance = await api.getBalance();
|
||||
return { p2p, balance }
|
||||
})
|
||||
return { p2p, balance };
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
api.onUpdateNotification([NotificationType.CoinWithdrawn], () => {
|
||||
@ -39,13 +47,15 @@ export function useComponentState(
|
||||
});
|
||||
});
|
||||
|
||||
const [operationError, setOperationError] = useState<TalerErrorDetail | undefined>(undefined)
|
||||
const [operationError, setOperationError] = useState<
|
||||
TalerErrorDetail | undefined
|
||||
>(undefined);
|
||||
|
||||
if (!hook) {
|
||||
return {
|
||||
status: "loading",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hook.hasError) {
|
||||
return {
|
||||
@ -56,13 +66,17 @@ export function useComponentState(
|
||||
|
||||
// const { payStatus } = hook.response.p2p;
|
||||
|
||||
const { amount: purseAmount, contractTerms, peerPullPaymentIncomingId } = hook.response.p2p
|
||||
const {
|
||||
amount: purseAmount,
|
||||
contractTerms,
|
||||
peerPullPaymentIncomingId,
|
||||
} = hook.response.p2p;
|
||||
|
||||
|
||||
const amountStr: string = contractTerms?.amount
|
||||
const amount = Amounts.parseOrThrow(amountStr)
|
||||
const summary: string | undefined = contractTerms?.summary
|
||||
const expiration: TalerProtocolTimestamp | undefined = contractTerms?.purse_expiration
|
||||
const amountStr: string = contractTerms?.amount;
|
||||
const amount = Amounts.parseOrThrow(amountStr);
|
||||
const summary: string | undefined = contractTerms?.summary;
|
||||
const expiration: TalerProtocolTimestamp | undefined =
|
||||
contractTerms?.purse_expiration;
|
||||
|
||||
const foundBalance = hook.response.balance.balances.find(
|
||||
(b) => Amounts.parseOrThrow(b.available).currency === amount.currency,
|
||||
@ -71,35 +85,32 @@ export function useComponentState(
|
||||
const paymentPossible: PreparePayResult = {
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
proposalId: "fakeID",
|
||||
contractTerms: {
|
||||
} as any,
|
||||
contractTerms: {} as any,
|
||||
contractTermsHash: "asd",
|
||||
amountRaw: hook.response.p2p.amount,
|
||||
amountEffective: hook.response.p2p.amount,
|
||||
noncePriv: "",
|
||||
} as PreparePayResult
|
||||
} as PreparePayResult;
|
||||
|
||||
const insufficientBalance: PreparePayResult = {
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
proposalId: "fakeID",
|
||||
contractTerms: {
|
||||
} as any,
|
||||
contractTerms: {} as any,
|
||||
amountRaw: hook.response.p2p.amount,
|
||||
noncePriv: "",
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const baseResult = {
|
||||
uri: talerPayPullUri,
|
||||
cancel: {
|
||||
onClick: onClose
|
||||
onClick: onClose,
|
||||
},
|
||||
amount,
|
||||
goToWalletManualWithdraw,
|
||||
summary,
|
||||
expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined,
|
||||
operationError,
|
||||
}
|
||||
};
|
||||
|
||||
if (!foundBalance) {
|
||||
return {
|
||||
@ -108,20 +119,21 @@ export function useComponentState(
|
||||
balance: undefined,
|
||||
...baseResult,
|
||||
payStatus: insufficientBalance,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const foundAmount = Amounts.parseOrThrow(foundBalance.available);
|
||||
|
||||
//FIXME: should use pay result type since it check for coins exceptions
|
||||
if (Amounts.cmp(foundAmount, amount) < 0) { //payStatus.status === PreparePayResultType.InsufficientBalance) {
|
||||
if (Amounts.cmp(foundAmount, amount) < 0) {
|
||||
//payStatus.status === PreparePayResultType.InsufficientBalance) {
|
||||
return {
|
||||
status: 'no-enough-balance',
|
||||
status: "no-enough-balance",
|
||||
error: undefined,
|
||||
balance: foundAmount,
|
||||
...baseResult,
|
||||
payStatus: insufficientBalance,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
|
||||
@ -135,19 +147,18 @@ export function useComponentState(
|
||||
async function accept(): Promise<void> {
|
||||
try {
|
||||
const resp = await api.acceptPeerPullPayment({
|
||||
peerPullPaymentIncomingId
|
||||
})
|
||||
await onClose()
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
await onClose();
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail)
|
||||
setOperationError(e.errorDetail);
|
||||
}
|
||||
console.error(e)
|
||||
throw Error("error trying to accept")
|
||||
console.error(e);
|
||||
throw Error("error trying to accept");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
status: "ready",
|
||||
error: undefined,
|
||||
@ -155,7 +166,7 @@ export function useComponentState(
|
||||
payStatus: paymentPossible,
|
||||
balance: foundAmount,
|
||||
accept: {
|
||||
onClick: accept
|
||||
onClick: accept,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("test description", () => {
|
||||
|
||||
it("should assert", () => {
|
||||
|
||||
expect([]).deep.equals([])
|
||||
expect([]).deep.equals([]);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -14,7 +14,14 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AmountJson, ConfirmPayResult, PreparePayResult, PreparePayResultAlreadyConfirmed, PreparePayResultInsufficientBalance, PreparePayResultPaymentPossible } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AmountJson,
|
||||
ConfirmPayResult,
|
||||
PreparePayResult,
|
||||
PreparePayResultAlreadyConfirmed,
|
||||
PreparePayResultInsufficientBalance,
|
||||
PreparePayResultPaymentPossible,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -24,8 +31,6 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { LoadingUriView, BaseView } from "./views.js";
|
||||
|
||||
|
||||
|
||||
export interface Props {
|
||||
talerPayUri?: string;
|
||||
goToWalletManualWithdraw: (amount?: string) => Promise<void>;
|
||||
@ -42,7 +47,6 @@ export type State =
|
||||
| State.Confirmed;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -60,12 +64,12 @@ export namespace State {
|
||||
cancel: () => Promise<void>;
|
||||
}
|
||||
export interface NoBalanceForCurrency extends BaseInfo {
|
||||
status: "no-balance-for-currency"
|
||||
status: "no-balance-for-currency";
|
||||
payStatus: PreparePayResult;
|
||||
balance: undefined;
|
||||
}
|
||||
export interface NoEnoughBalance extends BaseInfo {
|
||||
status: "no-enough-balance"
|
||||
status: "no-enough-balance";
|
||||
payStatus: PreparePayResult;
|
||||
balance: AmountJson;
|
||||
}
|
||||
@ -101,4 +105,8 @@ const viewMapping: StateViewMap<State> = {
|
||||
ready: BaseView,
|
||||
};
|
||||
|
||||
export const PaymentPage = compose("Payment", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
export const PaymentPage = compose(
|
||||
"Payment",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,8 +14,15 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { AmountJson, Amounts, ConfirmPayResult, ConfirmPayResultType, NotificationType, PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
ConfirmPayResult,
|
||||
ConfirmPayResultType,
|
||||
NotificationType,
|
||||
PreparePayResultType,
|
||||
TalerErrorCode,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -82,8 +89,9 @@ export function useComponentState(
|
||||
uri: hook.response.uri,
|
||||
amount,
|
||||
error: undefined,
|
||||
cancel, goToWalletManualWithdraw
|
||||
}
|
||||
cancel,
|
||||
goToWalletManualWithdraw,
|
||||
};
|
||||
|
||||
if (!foundBalance) {
|
||||
return {
|
||||
@ -91,7 +99,7 @@ export function useComponentState(
|
||||
balance: undefined,
|
||||
payStatus,
|
||||
...baseResult,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const foundAmount = Amounts.parseOrThrow(foundBalance.available);
|
||||
@ -109,11 +117,11 @@ export function useComponentState(
|
||||
|
||||
if (payStatus.status === PreparePayResultType.InsufficientBalance) {
|
||||
return {
|
||||
status: 'no-enough-balance',
|
||||
status: "no-enough-balance",
|
||||
balance: foundAmount,
|
||||
payStatus,
|
||||
...baseResult,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
|
||||
@ -125,7 +133,6 @@ export function useComponentState(
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function doPayment(): Promise<void> {
|
||||
try {
|
||||
if (payStatus.status !== "payment-possible") {
|
||||
@ -169,8 +176,6 @@ export function useComponentState(
|
||||
payHandler,
|
||||
payStatus,
|
||||
...baseResult,
|
||||
balance: foundAmount
|
||||
balance: foundAmount,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -70,9 +70,16 @@ describe("Payment CTA states", () => {
|
||||
it("should tell the user that the URI is missing", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: undefined, cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: undefined,
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -98,18 +105,25 @@ describe("Payment CTA states", () => {
|
||||
it("should response with no balance", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -133,22 +147,29 @@ describe("Payment CTA states", () => {
|
||||
it("should not be able to pay if there is no enough balance", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:5",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:5",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -172,23 +193,30 @@ describe("Payment CTA states", () => {
|
||||
it("should be able to pay (without fee)", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -214,23 +242,30 @@ describe("Payment CTA states", () => {
|
||||
it("should be able to pay (with fee)", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -256,28 +291,35 @@ describe("Payment CTA states", () => {
|
||||
it("should get confirmation done after pay successfully", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {},
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {},
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -317,28 +359,35 @@ describe("Payment CTA states", () => {
|
||||
it("should not stay in ready state after pay with error", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Pending,
|
||||
lastError: { code: 1 },
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Pending,
|
||||
lastError: { code: 1 },
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -393,23 +442,30 @@ describe("Payment CTA states", () => {
|
||||
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerPayUri: "taller://pay", cancel: nullFunction, goToWalletManualWithdraw: nullFunction }, {
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: Amounts.stringify(availableBalance),
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
},
|
||||
{
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: Amounts.stringify(availableBalance),
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
|
@ -21,9 +21,13 @@ import { ButtonHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { CompletedView, IgnoredView, InProgressView, LoadingUriView, ReadyView } from "./views.js";
|
||||
|
||||
|
||||
import {
|
||||
CompletedView,
|
||||
IgnoredView,
|
||||
InProgressView,
|
||||
LoadingUriView,
|
||||
ReadyView,
|
||||
} from "./views.js";
|
||||
|
||||
export interface Props {
|
||||
talerRefundUri?: string;
|
||||
@ -39,7 +43,6 @@ export type State =
|
||||
| State.Completed;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -75,13 +78,11 @@ export namespace State {
|
||||
export interface InProgress extends BaseInfo {
|
||||
status: "in-progress";
|
||||
error: undefined;
|
||||
|
||||
}
|
||||
export interface Completed extends BaseInfo {
|
||||
status: "completed";
|
||||
error: undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
@ -93,4 +94,8 @@ const viewMapping: StateViewMap<State> = {
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
export const RefundPage = compose("Refund", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
export const RefundPage = compose(
|
||||
"Refund",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,6 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { Amounts, NotificationType } from "@gnu-taler/taler-util";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -40,7 +39,7 @@ export function useComponentState(
|
||||
});
|
||||
|
||||
if (!info) {
|
||||
return { status: "loading", error: undefined }
|
||||
return { status: "loading", error: undefined };
|
||||
}
|
||||
if (info.hasError) {
|
||||
return {
|
||||
@ -67,7 +66,7 @@ export function useComponentState(
|
||||
products: info.response.refund.info.products,
|
||||
awaitingAmount: Amounts.parseOrThrow(refund.awaiting),
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
|
||||
if (ignored) {
|
||||
return {
|
||||
|
@ -21,8 +21,9 @@
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts, NotificationType,
|
||||
PrepareRefundResult
|
||||
Amounts,
|
||||
NotificationType,
|
||||
PrepareRefundResult,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { expect } from "chai";
|
||||
import { mountHook } from "../../test-utils.js";
|
||||
@ -33,11 +34,19 @@ describe("Refund CTA states", () => {
|
||||
it("should tell the user that the URI is missing", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerRefundUri: undefined, cancel: async () => { null } }, {
|
||||
prepareRefund: async () => ({}),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerRefundUri: undefined,
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareRefund: async () => ({}),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -64,27 +73,35 @@ describe("Refund CTA states", () => {
|
||||
it("should be ready after loading", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerRefundUri: "taler://refund/asdasdas", cancel: async () => { null } }, {
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
useComponentState(
|
||||
{
|
||||
talerRefundUri: "taler://refund/asdasdas",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any),
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -113,27 +130,35 @@ describe("Refund CTA states", () => {
|
||||
it("should be ignored after clicking the ignore button", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerRefundUri: "taler://refund/asdasdas", cancel: async () => { null } }, {
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
useComponentState(
|
||||
{
|
||||
talerRefundUri: "taler://refund/asdasdas",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any),
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -189,27 +214,35 @@ describe("Refund CTA states", () => {
|
||||
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerRefundUri: "taler://refund/asdasdas", cancel: async () => { null } }, {
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
awaiting: Amounts.stringify(awaiting),
|
||||
effectivePaid: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: Amounts.stringify(granted),
|
||||
pending,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
useComponentState(
|
||||
{
|
||||
talerRefundUri: "taler://refund/asdasdas",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
} as any),
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
awaiting: Amounts.stringify(awaiting),
|
||||
effectivePaid: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: Amounts.stringify(granted),
|
||||
pending,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
|
@ -20,13 +20,14 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import {
|
||||
Props as TermsOfServiceSectionProps
|
||||
} from "../TermsOfServiceSection.js";
|
||||
import { Props as TermsOfServiceSectionProps } from "../TermsOfServiceSection.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { AcceptedView, IgnoredView, LoadingUriView, ReadyView } from "./views.js";
|
||||
|
||||
|
||||
import {
|
||||
AcceptedView,
|
||||
IgnoredView,
|
||||
LoadingUriView,
|
||||
ReadyView,
|
||||
} from "./views.js";
|
||||
|
||||
export interface Props {
|
||||
talerTipUri?: string;
|
||||
@ -42,7 +43,6 @@ export type State =
|
||||
| State.Ignored;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -77,9 +77,13 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"accepted": AcceptedView,
|
||||
"ignored": IgnoredView,
|
||||
"ready": ReadyView,
|
||||
accepted: AcceptedView,
|
||||
ignored: IgnoredView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
export const TipPage = compose("Tip", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
export const TipPage = compose(
|
||||
"Tip",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,6 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { Amounts } from "@gnu-taler/taler-util";
|
||||
import { useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -37,7 +36,7 @@ export function useComponentState(
|
||||
return {
|
||||
status: "loading",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (tipInfo.hasError) {
|
||||
return {
|
||||
@ -59,9 +58,9 @@ export function useComponentState(
|
||||
amount: Amounts.parseOrThrow(tip.tipAmountEffective),
|
||||
error: undefined,
|
||||
cancel: {
|
||||
onClick: onCancel
|
||||
}
|
||||
}
|
||||
onClick: onCancel,
|
||||
},
|
||||
};
|
||||
|
||||
if (tipIgnored) {
|
||||
return {
|
||||
@ -85,4 +84,3 @@ export function useComponentState(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,7 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import {
|
||||
Amounts, PrepareTipResult
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Amounts, PrepareTipResult } from "@gnu-taler/taler-util";
|
||||
import { expect } from "chai";
|
||||
import { mountHook } from "../../test-utils.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
@ -30,10 +28,18 @@ describe("Tip CTA states", () => {
|
||||
it("should tell the user that the URI is missing", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerTipUri: undefined, onCancel: async () => { null } }, {
|
||||
prepareTip: async () => ({}),
|
||||
acceptTip: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerTipUri: undefined,
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareTip: async () => ({}),
|
||||
acceptTip: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -62,19 +68,27 @@ describe("Tip CTA states", () => {
|
||||
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerTipUri: "taler://tip/asd", onCancel: async () => { null } }, {
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: tipAccepted,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => {
|
||||
tipAccepted = true;
|
||||
useComponentState(
|
||||
{
|
||||
talerTipUri: "taler://tip/asd",
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
} as any),
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: tipAccepted,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => {
|
||||
tipAccepted = true;
|
||||
},
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -114,16 +128,24 @@ describe("Tip CTA states", () => {
|
||||
it("should be ignored after clicking the ignore button", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerTipUri: "taler://tip/asd", onCancel: async () => { null } }, {
|
||||
prepareTip: async () =>
|
||||
({
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerTipUri: "taler://tip/asd",
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -160,17 +182,25 @@ describe("Tip CTA states", () => {
|
||||
it("should render accepted if the tip has been used previously", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState({ talerTipUri: "taler://tip/asd", onCancel: async () => { null } }, {
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: true,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any),
|
||||
useComponentState(
|
||||
{
|
||||
talerTipUri: "taler://tip/asd",
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: true,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
|
@ -35,7 +35,6 @@ export type State =
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -59,9 +58,9 @@ export namespace State {
|
||||
status: "ready";
|
||||
invalid: boolean;
|
||||
create: ButtonHandler;
|
||||
toBeReceived: AmountJson,
|
||||
chosenAmount: AmountJson,
|
||||
subject: TextFieldHandler,
|
||||
toBeReceived: AmountJson;
|
||||
chosenAmount: AmountJson;
|
||||
subject: TextFieldHandler;
|
||||
error: undefined;
|
||||
operationError?: TalerErrorDetail;
|
||||
}
|
||||
@ -70,10 +69,12 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"created": CreatedView,
|
||||
"ready": ReadyView,
|
||||
created: CreatedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
||||
export const TransferCreatePage = compose("TransferCreatePage", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
|
||||
export const TransferCreatePage = compose(
|
||||
"TransferCreatePage",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -24,11 +24,13 @@ export function useComponentState(
|
||||
{ amount: amountStr, onClose }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const amount = Amounts.parseOrThrow(amountStr)
|
||||
const amount = Amounts.parseOrThrow(amountStr);
|
||||
|
||||
const [subject, setSubject] = useState("");
|
||||
const [talerUri, setTalerUri] = useState("")
|
||||
const [operationError, setOperationError] = useState<TalerErrorDetail | undefined>(undefined)
|
||||
const [talerUri, setTalerUri] = useState("");
|
||||
const [operationError, setOperationError] = useState<
|
||||
TalerErrorDetail | undefined
|
||||
>(undefined);
|
||||
|
||||
if (talerUri) {
|
||||
return {
|
||||
@ -41,28 +43,26 @@ export function useComponentState(
|
||||
copyToClipboard: {
|
||||
onClick: async () => {
|
||||
navigator.clipboard.writeText(talerUri);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function accept(): Promise<string> {
|
||||
try {
|
||||
|
||||
const resp = await api.initiatePeerPushPayment({
|
||||
amount: Amounts.stringify(amount),
|
||||
partialContractTerms: {
|
||||
summary: subject
|
||||
}
|
||||
})
|
||||
return resp.talerUri
|
||||
summary: subject,
|
||||
},
|
||||
});
|
||||
return resp.talerUri;
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail)
|
||||
setOperationError(e.errorDetail);
|
||||
}
|
||||
console.error(e)
|
||||
throw Error("error trying to accept")
|
||||
console.error(e);
|
||||
throw Error("error trying to accept");
|
||||
}
|
||||
}
|
||||
return {
|
||||
@ -74,17 +74,17 @@ export function useComponentState(
|
||||
subject: {
|
||||
error: !subject ? "cant be empty" : undefined,
|
||||
value: subject,
|
||||
onInput: async (e) => setSubject(e)
|
||||
onInput: async (e) => setSubject(e),
|
||||
},
|
||||
create: {
|
||||
onClick: async () => {
|
||||
const uri = await accept();
|
||||
setTalerUri(uri)
|
||||
}
|
||||
setTalerUri(uri);
|
||||
},
|
||||
},
|
||||
chosenAmount: amount,
|
||||
toBeReceived: amount,
|
||||
error: undefined,
|
||||
operationError
|
||||
}
|
||||
operationError,
|
||||
};
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("test description", () => {
|
||||
|
||||
it("should assert", () => {
|
||||
|
||||
expect([]).deep.equals([])
|
||||
expect([]).deep.equals([]);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -14,7 +14,11 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AbsoluteTime, AmountJson, TalerErrorDetail } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AbsoluteTime,
|
||||
AmountJson,
|
||||
TalerErrorDetail,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { ButtonHandler } from "../../mui/handlers.js";
|
||||
@ -28,13 +32,9 @@ export interface Props {
|
||||
onClose: () => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
| State.Loading
|
||||
| State.LoadingUriError
|
||||
| State.Ready;
|
||||
export type State = State.Loading | State.LoadingUriError | State.Ready;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -51,7 +51,7 @@ export namespace State {
|
||||
}
|
||||
export interface Ready extends BaseInfo {
|
||||
status: "ready";
|
||||
amount: AmountJson,
|
||||
amount: AmountJson;
|
||||
summary: string | undefined;
|
||||
expiration: AbsoluteTime | undefined;
|
||||
error: undefined;
|
||||
@ -63,9 +63,11 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"ready": ReadyView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
||||
export const TransferPickupPage = compose("TransferPickupPage", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
|
||||
export const TransferPickupPage = compose(
|
||||
"TransferPickupPage",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,12 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AbsoluteTime, Amounts, TalerErrorDetail, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AbsoluteTime,
|
||||
Amounts,
|
||||
TalerErrorDetail,
|
||||
TalerProtocolTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -28,15 +33,17 @@ export function useComponentState(
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
return await api.checkPeerPushPayment({
|
||||
talerUri: talerPayPushUri,
|
||||
})
|
||||
}, [])
|
||||
const [operationError, setOperationError] = useState<TalerErrorDetail | undefined>(undefined)
|
||||
});
|
||||
}, []);
|
||||
const [operationError, setOperationError] = useState<
|
||||
TalerErrorDetail | undefined
|
||||
>(undefined);
|
||||
|
||||
if (!hook) {
|
||||
return {
|
||||
status: "loading",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hook.hasError) {
|
||||
return {
|
||||
@ -45,24 +52,29 @@ export function useComponentState(
|
||||
};
|
||||
}
|
||||
|
||||
const { amount: purseAmount, contractTerms, peerPushPaymentIncomingId } = hook.response
|
||||
const {
|
||||
amount: purseAmount,
|
||||
contractTerms,
|
||||
peerPushPaymentIncomingId,
|
||||
} = hook.response;
|
||||
|
||||
const amount: string = contractTerms?.amount
|
||||
const summary: string | undefined = contractTerms?.summary
|
||||
const expiration: TalerProtocolTimestamp | undefined = contractTerms?.purse_expiration
|
||||
const amount: string = contractTerms?.amount;
|
||||
const summary: string | undefined = contractTerms?.summary;
|
||||
const expiration: TalerProtocolTimestamp | undefined =
|
||||
contractTerms?.purse_expiration;
|
||||
|
||||
async function accept(): Promise<void> {
|
||||
try {
|
||||
const resp = await api.acceptPeerPushPayment({
|
||||
peerPushPaymentIncomingId
|
||||
})
|
||||
await onClose()
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
await onClose();
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail)
|
||||
setOperationError(e.errorDetail);
|
||||
}
|
||||
console.error(e)
|
||||
throw Error("error trying to accept")
|
||||
console.error(e);
|
||||
throw Error("error trying to accept");
|
||||
}
|
||||
}
|
||||
return {
|
||||
@ -70,13 +82,13 @@ export function useComponentState(
|
||||
amount: Amounts.parseOrThrow(amount),
|
||||
error: undefined,
|
||||
accept: {
|
||||
onClick: accept
|
||||
onClick: accept,
|
||||
},
|
||||
summary,
|
||||
expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined,
|
||||
cancel: {
|
||||
onClick: onClose
|
||||
onClick: onClose,
|
||||
},
|
||||
operationError
|
||||
}
|
||||
operationError,
|
||||
};
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("test description", () => {
|
||||
|
||||
it("should assert", () => {
|
||||
|
||||
expect([]).deep.equals([])
|
||||
expect([]).deep.equals([]);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -20,12 +20,18 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { Props as TermsOfServiceSectionProps } from "../TermsOfServiceSection.js";
|
||||
import {
|
||||
Props as TermsOfServiceSectionProps
|
||||
} from "../TermsOfServiceSection.js";
|
||||
import { useComponentStateFromParams, useComponentStateFromURI } from "./state.js";
|
||||
import { CompletedView, LoadingExchangeView, LoadingInfoView, LoadingUriView, SuccessView } from "./views.js";
|
||||
|
||||
useComponentStateFromParams,
|
||||
useComponentStateFromURI,
|
||||
} from "./state.js";
|
||||
import {
|
||||
CompletedView,
|
||||
LoadingExchangeView,
|
||||
LoadingInfoView,
|
||||
LoadingUriView,
|
||||
SuccessView,
|
||||
} from "./views.js";
|
||||
|
||||
export interface PropsFromURI {
|
||||
talerWithdrawUri: string | undefined;
|
||||
@ -46,7 +52,6 @@ export type State =
|
||||
| State.Completed;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -99,5 +104,13 @@ const viewMapping: StateViewMap<State> = {
|
||||
success: SuccessView,
|
||||
};
|
||||
|
||||
export const WithdrawPageFromURI = compose("WithdrawPageFromURI", (p: PropsFromURI) => useComponentStateFromURI(p, wxApi), viewMapping)
|
||||
export const WithdrawPageFromParams = compose("WithdrawPageFromParams", (p: PropsFromParams) => useComponentStateFromParams(p, wxApi), viewMapping)
|
||||
export const WithdrawPageFromURI = compose(
|
||||
"WithdrawPageFromURI",
|
||||
(p: PropsFromURI) => useComponentStateFromURI(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
export const WithdrawPageFromParams = compose(
|
||||
"WithdrawPageFromParams",
|
||||
(p: PropsFromParams) => useComponentStateFromParams(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,6 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { useState } from "preact/hooks";
|
||||
@ -27,7 +26,6 @@ export function useComponentStateFromParams(
|
||||
{ amount, cancel }: PropsFromParams,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
|
||||
const [ageRestricted, setAgeRestricted] = useState(0);
|
||||
|
||||
const exchangeHook = useAsyncAsHook(api.listExchanges);
|
||||
@ -40,14 +38,20 @@ export function useComponentStateFromParams(
|
||||
const chosenAmount = Amounts.parseOrThrow(amount);
|
||||
|
||||
// get the first exchange with the currency as the default one
|
||||
const exchange = exchangeHookDep ? exchangeHookDep.exchanges.find(e => e.currency === chosenAmount.currency) : undefined
|
||||
const exchange = exchangeHookDep
|
||||
? exchangeHookDep.exchanges.find(
|
||||
(e) => e.currency === chosenAmount.currency,
|
||||
)
|
||||
: undefined;
|
||||
/**
|
||||
* For the exchange selected, bring the status of the terms of service
|
||||
*/
|
||||
const terms = useAsyncAsHook(async () => {
|
||||
if (!exchange) return undefined
|
||||
if (!exchange) return undefined;
|
||||
|
||||
const exchangeTos = await api.getExchangeTos(exchange.exchangeBaseUrl, ["text/xml"]);
|
||||
const exchangeTos = await api.getExchangeTos(exchange.exchangeBaseUrl, [
|
||||
"text/xml",
|
||||
]);
|
||||
|
||||
const state = buildTermsOfServiceState(exchangeTos);
|
||||
|
||||
@ -59,7 +63,7 @@ export function useComponentStateFromParams(
|
||||
* about the withdrawal
|
||||
*/
|
||||
const amountHook = useAsyncAsHook(async () => {
|
||||
if (!exchange) return undefined
|
||||
if (!exchange) return undefined;
|
||||
|
||||
const info = await api.getExchangeWithdrawalInfo({
|
||||
exchangeBaseUrl: exchange.exchangeBaseUrl,
|
||||
@ -71,9 +75,12 @@ export function useComponentStateFromParams(
|
||||
const withdrawAmount = {
|
||||
raw: Amounts.parseOrThrow(info.withdrawalAmountRaw),
|
||||
effective: Amounts.parseOrThrow(info.withdrawalAmountEffective),
|
||||
}
|
||||
};
|
||||
|
||||
return { amount: withdrawAmount, ageRestrictionOptions: info.ageRestrictionOptions };
|
||||
return {
|
||||
amount: withdrawAmount,
|
||||
ageRestrictionOptions: info.ageRestrictionOptions,
|
||||
};
|
||||
}, [exchangeHookDep]);
|
||||
|
||||
const [reviewing, setReviewing] = useState<boolean>(false);
|
||||
@ -85,7 +92,7 @@ export function useComponentStateFromParams(
|
||||
const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false);
|
||||
const [withdrawCompleted, setWithdrawCompleted] = useState<boolean>(false);
|
||||
|
||||
if (!exchangeHook) return { status: "loading", error: undefined }
|
||||
if (!exchangeHook) return { status: "loading", error: undefined };
|
||||
if (exchangeHook.hasError) {
|
||||
return {
|
||||
status: "loading-uri",
|
||||
@ -125,7 +132,7 @@ export function useComponentStateFromParams(
|
||||
}
|
||||
|
||||
if (!amountHook) {
|
||||
return { status: "loading", error: undefined }
|
||||
return { status: "loading", error: undefined };
|
||||
}
|
||||
if (amountHook.hasError) {
|
||||
return {
|
||||
@ -149,8 +156,8 @@ export function useComponentStateFromParams(
|
||||
const { state: termsState } = (!terms
|
||||
? undefined
|
||||
: terms.hasError
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
|
||||
async function onAccept(accepted: boolean): Promise<void> {
|
||||
if (!termsState || !exchange) return;
|
||||
@ -173,21 +180,25 @@ export function useComponentStateFromParams(
|
||||
termsState !== undefined &&
|
||||
(termsState.status === "changed" || termsState.status === "new");
|
||||
|
||||
const ageRestrictionOptions = amountHook.response.
|
||||
ageRestrictionOptions?.
|
||||
reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {} as Record<string, string>)
|
||||
const ageRestrictionOptions =
|
||||
amountHook.response.ageRestrictionOptions?.reduce(
|
||||
(p, c) => ({ ...p, [c]: `under ${c}` }),
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
const ageRestrictionEnabled = ageRestrictionOptions !== undefined
|
||||
const ageRestrictionEnabled = ageRestrictionOptions !== undefined;
|
||||
if (ageRestrictionEnabled) {
|
||||
ageRestrictionOptions["0"] = "Not restricted";
|
||||
}
|
||||
|
||||
//TODO: calculate based on exchange info
|
||||
const ageRestriction = ageRestrictionEnabled ? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
} : undefined;
|
||||
const ageRestriction = ageRestrictionEnabled
|
||||
? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
@ -207,12 +218,12 @@ export function useComponentStateFromParams(
|
||||
tosProps: !termsState
|
||||
? undefined
|
||||
: {
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
mustAcceptFirst,
|
||||
cancel,
|
||||
};
|
||||
@ -233,7 +244,7 @@ export function useComponentStateFromURI(
|
||||
const uriInfo = await api.getWithdrawalDetailsForUri({
|
||||
talerWithdrawUri,
|
||||
});
|
||||
const { amount, defaultExchangeBaseUrl } = uriInfo
|
||||
const { amount, defaultExchangeBaseUrl } = uriInfo;
|
||||
return { amount, thisExchange: defaultExchangeBaseUrl };
|
||||
});
|
||||
|
||||
@ -245,14 +256,15 @@ export function useComponentStateFromURI(
|
||||
? undefined
|
||||
: uriInfoHook.response;
|
||||
|
||||
|
||||
/**
|
||||
* For the exchange selected, bring the status of the terms of service
|
||||
*/
|
||||
const terms = useAsyncAsHook(async () => {
|
||||
if (!uriHookDep?.thisExchange) return false;
|
||||
|
||||
const exchangeTos = await api.getExchangeTos(uriHookDep.thisExchange, ["text/xml"]);
|
||||
const exchangeTos = await api.getExchangeTos(uriHookDep.thisExchange, [
|
||||
"text/xml",
|
||||
]);
|
||||
|
||||
const state = buildTermsOfServiceState(exchangeTos);
|
||||
|
||||
@ -276,9 +288,12 @@ export function useComponentStateFromURI(
|
||||
const withdrawAmount = {
|
||||
raw: Amounts.parseOrThrow(info.withdrawalAmountRaw),
|
||||
effective: Amounts.parseOrThrow(info.withdrawalAmountEffective),
|
||||
}
|
||||
};
|
||||
|
||||
return { amount: withdrawAmount, ageRestrictionOptions: info.ageRestrictionOptions };
|
||||
return {
|
||||
amount: withdrawAmount,
|
||||
ageRestrictionOptions: info.ageRestrictionOptions,
|
||||
};
|
||||
}, [uriHookDep]);
|
||||
|
||||
const [reviewing, setReviewing] = useState<boolean>(false);
|
||||
@ -290,7 +305,7 @@ export function useComponentStateFromURI(
|
||||
const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false);
|
||||
const [withdrawCompleted, setWithdrawCompleted] = useState<boolean>(false);
|
||||
|
||||
if (!uriInfoHook) return { status: "loading", error: undefined }
|
||||
if (!uriInfoHook) return { status: "loading", error: undefined };
|
||||
if (uriInfoHook.hasError) {
|
||||
return {
|
||||
status: "loading-uri",
|
||||
@ -298,7 +313,7 @@ export function useComponentStateFromURI(
|
||||
};
|
||||
}
|
||||
|
||||
const { amount, thisExchange } = uriInfoHook.response
|
||||
const { amount, thisExchange } = uriInfoHook.response;
|
||||
|
||||
const chosenAmount = Amounts.parseOrThrow(amount);
|
||||
|
||||
@ -339,7 +354,7 @@ export function useComponentStateFromURI(
|
||||
}
|
||||
|
||||
if (!amountHook) {
|
||||
return { status: "loading", error: undefined }
|
||||
return { status: "loading", error: undefined };
|
||||
}
|
||||
if (amountHook.hasError) {
|
||||
return {
|
||||
@ -363,8 +378,8 @@ export function useComponentStateFromURI(
|
||||
const { state: termsState } = (!terms
|
||||
? undefined
|
||||
: terms.hasError
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
|
||||
async function onAccept(accepted: boolean): Promise<void> {
|
||||
if (!termsState || !thisExchange) return;
|
||||
@ -387,21 +402,25 @@ export function useComponentStateFromURI(
|
||||
termsState !== undefined &&
|
||||
(termsState.status === "changed" || termsState.status === "new");
|
||||
|
||||
const ageRestrictionOptions = amountHook.response.
|
||||
ageRestrictionOptions?.
|
||||
reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {} as Record<string, string>)
|
||||
const ageRestrictionOptions =
|
||||
amountHook.response.ageRestrictionOptions?.reduce(
|
||||
(p, c) => ({ ...p, [c]: `under ${c}` }),
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
const ageRestrictionEnabled = ageRestrictionOptions !== undefined
|
||||
const ageRestrictionEnabled = ageRestrictionOptions !== undefined;
|
||||
if (ageRestrictionEnabled) {
|
||||
ageRestrictionOptions["0"] = "Not restricted";
|
||||
}
|
||||
|
||||
//TODO: calculate based on exchange info
|
||||
const ageRestriction = ageRestrictionEnabled ? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
} : undefined;
|
||||
const ageRestriction = ageRestrictionEnabled
|
||||
? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
@ -422,14 +441,13 @@ export function useComponentStateFromURI(
|
||||
tosProps: !termsState
|
||||
? undefined
|
||||
: {
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
mustAcceptFirst,
|
||||
cancel,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -62,13 +62,21 @@ describe("Withdraw CTA states", () => {
|
||||
it("should tell the user that the URI is missing", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentStateFromURI({ talerWithdrawUri: undefined, cancel: async () => { null } }, {
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
}),
|
||||
} as any),
|
||||
useComponentStateFromURI(
|
||||
{
|
||||
talerWithdrawUri: undefined,
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -94,13 +102,21 @@ describe("Withdraw CTA states", () => {
|
||||
it("should tell the user that there is not known exchange", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentStateFromURI({ talerWithdrawUri: "taler-withdraw://", cancel: async () => { null } }, {
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "EUR:2",
|
||||
possibleExchanges: [],
|
||||
}),
|
||||
} as any),
|
||||
useComponentStateFromURI(
|
||||
{
|
||||
talerWithdrawUri: "taler-withdraw://",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "EUR:2",
|
||||
possibleExchanges: [],
|
||||
}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -128,26 +144,34 @@ describe("Withdraw CTA states", () => {
|
||||
it("should be able to withdraw if tos are ok", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentStateFromURI({ talerWithdrawUri: "taler-withdraw://", cancel: async () => { null } }, {
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v1",
|
||||
}),
|
||||
} as any),
|
||||
useComponentStateFromURI(
|
||||
{
|
||||
talerWithdrawUri: "taler-withdraw://",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v1",
|
||||
}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
@ -194,27 +218,35 @@ describe("Withdraw CTA states", () => {
|
||||
it("should be accept the tos before withdraw", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentStateFromURI({ talerWithdrawUri: "taler-withdraw://", cancel: async () => { null } }, {
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v2",
|
||||
}),
|
||||
setExchangeTosAccepted: async () => ({}),
|
||||
} as any),
|
||||
useComponentStateFromURI(
|
||||
{
|
||||
talerWithdrawUri: "taler-withdraw://",
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({
|
||||
amount: "ARS:2",
|
||||
possibleExchanges: exchanges,
|
||||
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v2",
|
||||
}),
|
||||
setExchangeTosAccepted: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
);
|
||||
|
||||
{
|
||||
|
@ -55,7 +55,9 @@ async function handleClipboardPerm(
|
||||
// as the result of an input event ...
|
||||
let granted: boolean;
|
||||
try {
|
||||
granted = await platform.getPermissionsApi().requestClipboardPermissions();
|
||||
granted = await platform
|
||||
.getPermissionsApi()
|
||||
.requestClipboardPermissions();
|
||||
} catch (lastError) {
|
||||
onChange(false);
|
||||
throw lastError;
|
||||
|
@ -20,7 +20,6 @@ import { h, VNode } from "preact";
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("useTalerActionURL hook", () => {
|
||||
|
||||
it("should be set url to undefined when dismiss", async () => {
|
||||
const ctx = ({ children }: { children: any }): VNode => {
|
||||
return h(IoCProviderForTesting, {
|
||||
@ -46,7 +45,7 @@ describe("useTalerActionURL hook", () => {
|
||||
const [url, setDismissed] = getLastResultOrThrow();
|
||||
expect(url).deep.equals({
|
||||
location: "clipboard",
|
||||
uri: "qwe"
|
||||
uri: "qwe",
|
||||
});
|
||||
setDismissed(true);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { useIocContext } from "../context/iocContext.js";
|
||||
|
||||
export interface UriLocation {
|
||||
uri: string;
|
||||
location: "clipboard" | "activeTab"
|
||||
location: "clipboard" | "activeTab";
|
||||
}
|
||||
|
||||
export function useTalerActionURL(): [
|
||||
@ -37,7 +37,7 @@ export function useTalerActionURL(): [
|
||||
if (clipUri) {
|
||||
setTalerActionUrl({
|
||||
location: "clipboard",
|
||||
uri: clipUri
|
||||
uri: clipUri,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -45,7 +45,7 @@ export function useTalerActionURL(): [
|
||||
if (tabUri) {
|
||||
setTalerActionUrl({
|
||||
location: "activeTab",
|
||||
uri: tabUri
|
||||
uri: tabUri,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -119,8 +119,9 @@ function handleContainer(containerInfo: Container, props: ManagedModalProps) {
|
||||
el: container,
|
||||
});
|
||||
// Use computed style, here to get the real padding to add our scrollbar width.
|
||||
container.style.paddingRight = `${getPaddingRight(container) + scrollbarSize
|
||||
}px`;
|
||||
container.style.paddingRight = `${
|
||||
getPaddingRight(container) + scrollbarSize
|
||||
}px`;
|
||||
|
||||
// .mui-fixed is a global helper.
|
||||
const fixedElements =
|
||||
@ -131,8 +132,9 @@ function handleContainer(containerInfo: Container, props: ManagedModalProps) {
|
||||
property: "padding-right",
|
||||
el: element,
|
||||
});
|
||||
element.style.paddingRight = `${getPaddingRight(element) + scrollbarSize
|
||||
}px`;
|
||||
element.style.paddingRight = `${
|
||||
getPaddingRight(element) + scrollbarSize
|
||||
}px`;
|
||||
});
|
||||
}
|
||||
|
||||
@ -142,7 +144,7 @@ function handleContainer(containerInfo: Container, props: ManagedModalProps) {
|
||||
const containerWindow = ownerWindow(container);
|
||||
const scrollContainer =
|
||||
parent?.nodeName === "HTML" &&
|
||||
containerWindow.getComputedStyle(parent).overflowY === "scroll"
|
||||
containerWindow.getComputedStyle(parent).overflowY === "scroll"
|
||||
? parent
|
||||
: container;
|
||||
|
||||
|
@ -176,13 +176,13 @@ export interface PlatformAPI {
|
||||
findTalerUriInClipboard(): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Used from the frontend to send commands to the wallet
|
||||
*
|
||||
* @param operation
|
||||
* @param payload
|
||||
*
|
||||
* @return response from the backend
|
||||
*/
|
||||
* Used from the frontend to send commands to the wallet
|
||||
*
|
||||
* @param operation
|
||||
* @param payload
|
||||
*
|
||||
* @return response from the backend
|
||||
*/
|
||||
sendMessageToWalletBackground(
|
||||
operation: string,
|
||||
payload: any,
|
||||
|
@ -109,7 +109,7 @@ export async function requestClipboardPermissions(): Promise<boolean> {
|
||||
rej(le);
|
||||
}
|
||||
res(resp);
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -130,13 +130,13 @@ type HeaderListenerFunc = (
|
||||
) => void;
|
||||
let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
|
||||
|
||||
type TabListenerFunc = (
|
||||
tabId: number, info: chrome.tabs.TabChangeInfo,
|
||||
) => void;
|
||||
type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void;
|
||||
let currentTabListener: TabListenerFunc | undefined = undefined;
|
||||
|
||||
export function containsTalerHeaderListener(): boolean {
|
||||
return currentHeaderListener !== undefined || currentTabListener !== undefined;
|
||||
return (
|
||||
currentHeaderListener !== undefined || currentTabListener !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeHostPermissions(): Promise<boolean> {
|
||||
@ -147,9 +147,11 @@ export async function removeHostPermissions(): Promise<boolean> {
|
||||
) {
|
||||
chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
|
||||
}
|
||||
if (currentTabListener &&
|
||||
chrome?.tabs?.onUpdated?.hasListener(currentTabListener)) {
|
||||
chrome.tabs.onUpdated.removeListener(currentTabListener)
|
||||
if (
|
||||
currentTabListener &&
|
||||
chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
|
||||
) {
|
||||
chrome.tabs.onUpdated.removeListener(currentTabListener);
|
||||
}
|
||||
|
||||
currentHeaderListener = undefined;
|
||||
@ -413,20 +415,25 @@ function registerTalerHeaderListener(
|
||||
.map((h) => h.value)
|
||||
.filter((value): value is string => !!value);
|
||||
if (values.length > 0) {
|
||||
logger.info(`Found a Taler URI in a response header for the request ${details.url} from tab ${details.tabId}`)
|
||||
logger.info(
|
||||
`Found a Taler URI in a response header for the request ${details.url} from tab ${details.tabId}`,
|
||||
);
|
||||
callback(details.tabId, values[0]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async function tabListener(tabId: number, info: chrome.tabs.TabChangeInfo): Promise<void> {
|
||||
async function tabListener(
|
||||
tabId: number,
|
||||
info: chrome.tabs.TabChangeInfo,
|
||||
): Promise<void> {
|
||||
if (tabId < 0) return;
|
||||
if (info.status !== "complete") return;
|
||||
const uri = await findTalerUriInTab(tabId);
|
||||
if (!uri) return;
|
||||
logger.info(`Found a Taler URI in the tab ${tabId}`)
|
||||
callback(tabId, uri)
|
||||
logger.info(`Found a Taler URI in the tab ${tabId}`);
|
||||
callback(tabId, uri);
|
||||
}
|
||||
|
||||
const prevHeaderListener = currentHeaderListener;
|
||||
@ -442,14 +449,18 @@ function registerTalerHeaderListener(
|
||||
) {
|
||||
chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener);
|
||||
}
|
||||
if (prevTabListener && chrome?.tabs?.onUpdated?.hasListener(prevTabListener)) {
|
||||
chrome.tabs.onUpdated.removeListener(prevTabListener)
|
||||
if (
|
||||
prevTabListener &&
|
||||
chrome?.tabs?.onUpdated?.hasListener(prevTabListener)
|
||||
) {
|
||||
chrome.tabs.onUpdated.removeListener(prevTabListener);
|
||||
}
|
||||
|
||||
//if the result was positive, add the headerListener
|
||||
if (result) {
|
||||
const headersEvent: chrome.webRequest.WebResponseHeadersEvent | undefined =
|
||||
chrome?.webRequest?.onHeadersReceived;
|
||||
const headersEvent:
|
||||
| chrome.webRequest.WebResponseHeadersEvent
|
||||
| undefined = chrome?.webRequest?.onHeadersReceived;
|
||||
if (headersEvent) {
|
||||
headersEvent.addListener(headerListener, { urls: ["<all_urls>"] }, [
|
||||
"responseHeaders",
|
||||
@ -472,7 +483,6 @@ function registerTalerHeaderListener(
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const alertIcons = {
|
||||
@ -515,26 +525,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 {
|
||||
@ -547,7 +557,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 {
|
||||
@ -727,20 +737,23 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> {
|
||||
}
|
||||
|
||||
async function timeout(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
async function findTalerUriInClipboard(): Promise<string | undefined> {
|
||||
try {
|
||||
//It looks like clipboard promise does not return, so we need a timeout
|
||||
const textInClipboard = await Promise.any([
|
||||
timeout(100),
|
||||
window.navigator.clipboard.readText()
|
||||
])
|
||||
window.navigator.clipboard.readText(),
|
||||
]);
|
||||
if (!textInClipboard) return;
|
||||
return textInClipboard.startsWith("taler://") || textInClipboard.startsWith("taler+http://") ? textInClipboard : undefined
|
||||
return textInClipboard.startsWith("taler://") ||
|
||||
textInClipboard.startsWith("taler+http://")
|
||||
? textInClipboard
|
||||
: undefined;
|
||||
} catch (e) {
|
||||
logger.error("could not read clipboard", e)
|
||||
return undefined
|
||||
logger.error("could not read clipboard", e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,8 @@ function getJsonIfOk(r: Response): Promise<any> {
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Try another server: (${r.status}) ${r.statusText || "internal server error"
|
||||
`Try another server: (${r.status}) ${
|
||||
r.statusText || "internal server error"
|
||||
}`,
|
||||
);
|
||||
}
|
||||
@ -102,10 +103,10 @@ export function buildTermsOfServiceStatus(
|
||||
return !content
|
||||
? "notfound"
|
||||
: !acceptedVersion
|
||||
? "new"
|
||||
: acceptedVersion !== currentVersion
|
||||
? "changed"
|
||||
: "accepted";
|
||||
? "new"
|
||||
: acceptedVersion !== currentVersion
|
||||
? "changed"
|
||||
: "accepted";
|
||||
}
|
||||
|
||||
function parseTermsOfServiceContent(
|
||||
|
@ -62,9 +62,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:0` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:0` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: {} }),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
);
|
||||
@ -89,9 +89,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: {} }),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
);
|
||||
@ -111,19 +111,21 @@ describe("DepositPage states", () => {
|
||||
await assertNoPendingUpdate();
|
||||
});
|
||||
|
||||
const ibanPayto_str = "payto://iban/ES8877998399652238"
|
||||
const ibanPayto_str = "payto://iban/ES8877998399652238";
|
||||
const ibanPayto = { ibanPayto_str: parsePaytoUri(ibanPayto_str)! };
|
||||
const talerBankPayto_str = "payto://x-taler-bank/ES8877998399652238"
|
||||
const talerBankPayto = { talerBankPayto_str: parsePaytoUri(talerBankPayto_str)! };
|
||||
const talerBankPayto_str = "payto://x-taler-bank/ES8877998399652238";
|
||||
const talerBankPayto = {
|
||||
talerBankPayto_str: parsePaytoUri(talerBankPayto_str)!,
|
||||
};
|
||||
|
||||
it("should have status 'ready' but unable to deposit ", async () => {
|
||||
const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: ibanPayto }),
|
||||
} as Partial<typeof wxApi> as any),
|
||||
);
|
||||
@ -153,9 +155,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: ibanPayto }),
|
||||
getFeeForDeposit: withoutFee,
|
||||
} as Partial<typeof wxApi> as any),
|
||||
@ -202,9 +204,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: ibanPayto }),
|
||||
getFeeForDeposit: withSomeFee,
|
||||
} as Partial<typeof wxApi> as any),
|
||||
@ -252,9 +254,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:1` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({
|
||||
accounts: { ...ibanPayto, ...talerBankPayto },
|
||||
}),
|
||||
@ -338,9 +340,9 @@ describe("DepositPage states", () => {
|
||||
mountHook(() =>
|
||||
useComponentState(currency, nullFunction, nullFunction, {
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [{ available: `${currency}:15` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [{ available: `${currency}:15` }],
|
||||
} as Partial<BalancesResponse>),
|
||||
listKnownBankAccounts: async () => ({ accounts: ibanPayto }),
|
||||
getFeeForDeposit: withSomeFee,
|
||||
} as Partial<typeof wxApi> as any),
|
||||
|
@ -25,13 +25,9 @@ export interface Props {
|
||||
p: string;
|
||||
}
|
||||
|
||||
export type State =
|
||||
| State.Loading
|
||||
| State.LoadingUriError
|
||||
| State.Ready;
|
||||
export type State = State.Loading | State.LoadingUriError | State.Ready;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -54,10 +50,11 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"ready": ReadyView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
||||
export const ComponentName = compose("ComponentName", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
|
||||
|
||||
export const ComponentName = compose(
|
||||
"ComponentName",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -17,12 +17,9 @@
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ p }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
export function useComponentState({ p }: Props, api: typeof wxApi): State {
|
||||
return {
|
||||
status: "ready",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -22,10 +22,7 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("test description", () => {
|
||||
|
||||
it("should assert", () => {
|
||||
|
||||
expect([]).deep.equals([])
|
||||
expect([]).deep.equals([]);
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
@ -14,16 +14,25 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { FeeDescription, FeeDescriptionPair, AbsoluteTime, ExchangeFullDetails, OperationMap } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
FeeDescription,
|
||||
FeeDescriptionPair,
|
||||
AbsoluteTime,
|
||||
ExchangeFullDetails,
|
||||
OperationMap,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { ComparingView, LoadingUriView, NoExchangesView, ReadyView } from "./views.js";
|
||||
|
||||
|
||||
import {
|
||||
ComparingView,
|
||||
LoadingUriView,
|
||||
NoExchangesView,
|
||||
ReadyView,
|
||||
} from "./views.js";
|
||||
|
||||
export interface Props {
|
||||
currency?: string;
|
||||
@ -39,7 +48,6 @@ export type State =
|
||||
| State.NoExchanges;
|
||||
|
||||
export namespace State {
|
||||
|
||||
export interface Loading {
|
||||
status: "loading";
|
||||
error: undefined;
|
||||
@ -75,13 +83,16 @@ export namespace State {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"comparing": ComparingView,
|
||||
comparing: ComparingView,
|
||||
"no-exchanges": NoExchangesView,
|
||||
"ready": ReadyView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
export const ExchangeSelectionPage = compose("ExchangeSelectionPage", (p: Props) => useComponentState(p, wxApi), viewMapping)
|
||||
export const ExchangeSelectionPage = compose(
|
||||
"ExchangeSelectionPage",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,7 +14,6 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
import { FeeDescription, OperationMap } from "@gnu-taler/taler-util";
|
||||
import { createDenominationPairTimeline } from "@gnu-taler/taler-wallet-core";
|
||||
import { useState } from "preact/hooks";
|
||||
@ -26,26 +25,32 @@ export function useComponentState(
|
||||
{ onCancel, onSelection, currency }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const initialValue = 0
|
||||
const initialValue = 0;
|
||||
const [value, setValue] = useState(String(initialValue));
|
||||
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
const { exchanges } = await api.listExchanges()
|
||||
const { exchanges } = await api.listExchanges();
|
||||
|
||||
const selectedIdx = parseInt(value, 10)
|
||||
const selectedExchange = exchanges.length == 0 ? undefined : exchanges[selectedIdx]
|
||||
const selected = !selectedExchange ? undefined : await api.getExchangeDetailedInfo(selectedExchange.exchangeBaseUrl)
|
||||
const selectedIdx = parseInt(value, 10);
|
||||
const selectedExchange =
|
||||
exchanges.length == 0 ? undefined : exchanges[selectedIdx];
|
||||
const selected = !selectedExchange
|
||||
? undefined
|
||||
: await api.getExchangeDetailedInfo(selectedExchange.exchangeBaseUrl);
|
||||
|
||||
const initialExchange = selectedIdx === initialValue ? undefined : exchanges[initialValue]
|
||||
const original = !initialExchange ? undefined : await api.getExchangeDetailedInfo(initialExchange.exchangeBaseUrl)
|
||||
return { exchanges, selected, original }
|
||||
const initialExchange =
|
||||
selectedIdx === initialValue ? undefined : exchanges[initialValue];
|
||||
const original = !initialExchange
|
||||
? undefined
|
||||
: await api.getExchangeDetailedInfo(initialExchange.exchangeBaseUrl);
|
||||
return { exchanges, selected, original };
|
||||
});
|
||||
|
||||
if (!hook) {
|
||||
return {
|
||||
status: "loading",
|
||||
error: undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hook.hasError) {
|
||||
return {
|
||||
@ -60,11 +65,14 @@ export function useComponentState(
|
||||
//!selected <=> exchanges.length === 0
|
||||
return {
|
||||
status: "no-exchanges",
|
||||
error: undefined
|
||||
}
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const exchangeMap = exchanges.reduce((prev, cur, idx) => ({ ...prev, [cur.exchangeBaseUrl]: String(idx) }), {} as Record<string, string>)
|
||||
const exchangeMap = exchanges.reduce(
|
||||
(prev, cur, idx) => ({ ...prev, [cur.exchangeBaseUrl]: String(idx) }),
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
|
||||
if (!original) {
|
||||
// !original <=> selected == original
|
||||
@ -74,24 +82,36 @@ export function useComponentState(
|
||||
list: exchangeMap,
|
||||
value: value,
|
||||
onChange: async (v) => {
|
||||
setValue(v)
|
||||
}
|
||||
setValue(v);
|
||||
},
|
||||
},
|
||||
error: undefined,
|
||||
onClose: {
|
||||
onClick: onCancel
|
||||
onClick: onCancel,
|
||||
},
|
||||
selected,
|
||||
timeline: selected.feesDescription
|
||||
}
|
||||
timeline: selected.feesDescription,
|
||||
};
|
||||
}
|
||||
|
||||
const pairTimeline: OperationMap<FeeDescription[]> = {
|
||||
deposit: createDenominationPairTimeline(selected.feesDescription.deposit, original.feesDescription.deposit),
|
||||
refresh: createDenominationPairTimeline(selected.feesDescription.refresh, original.feesDescription.refresh),
|
||||
refund: createDenominationPairTimeline(selected.feesDescription.refund, original.feesDescription.refund),
|
||||
withdraw: createDenominationPairTimeline(selected.feesDescription.withdraw, original.feesDescription.withdraw),
|
||||
}
|
||||
deposit: createDenominationPairTimeline(
|
||||
selected.feesDescription.deposit,
|
||||
original.feesDescription.deposit,
|
||||
),
|
||||
refresh: createDenominationPairTimeline(
|
||||
selected.feesDescription.refresh,
|
||||
original.feesDescription.refresh,
|
||||
),
|
||||
refund: createDenominationPairTimeline(
|
||||
selected.feesDescription.refund,
|
||||
original.feesDescription.refund,
|
||||
),
|
||||
withdraw: createDenominationPairTimeline(
|
||||
selected.feesDescription.withdraw,
|
||||
original.feesDescription.withdraw,
|
||||
),
|
||||
};
|
||||
|
||||
return {
|
||||
status: "comparing",
|
||||
@ -99,23 +119,21 @@ export function useComponentState(
|
||||
list: exchangeMap,
|
||||
value: value,
|
||||
onChange: async (v) => {
|
||||
setValue(v)
|
||||
}
|
||||
setValue(v);
|
||||
},
|
||||
},
|
||||
error: undefined,
|
||||
onReset: {
|
||||
onClick: async () => {
|
||||
setValue(String(initialValue))
|
||||
}
|
||||
setValue(String(initialValue));
|
||||
},
|
||||
},
|
||||
onSelect: {
|
||||
onClick: async () => {
|
||||
onSelection(selected.exchangeBaseUrl)
|
||||
}
|
||||
onSelection(selected.exchangeBaseUrl);
|
||||
},
|
||||
},
|
||||
selected,
|
||||
pairTimeline,
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,5 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import {
|
||||
AbsoluteTime,
|
||||
Amounts, DenominationInfo
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { AbsoluteTime, Amounts, DenominationInfo } from "@gnu-taler/taler-util";
|
||||
import { expect } from "chai";
|
||||
|
@ -261,9 +261,11 @@ export function listExchanges(): Promise<ExchangesListResponse> {
|
||||
return callBackend("listExchanges", {});
|
||||
}
|
||||
|
||||
export function getExchangeDetailedInfo(exchangeBaseUrl: string): Promise<ExchangeFullDetails> {
|
||||
export function getExchangeDetailedInfo(
|
||||
exchangeBaseUrl: string,
|
||||
): Promise<ExchangeFullDetails> {
|
||||
return callBackend("getExchangeDetailedInfo", {
|
||||
exchangeBaseUrl
|
||||
exchangeBaseUrl,
|
||||
});
|
||||
}
|
||||
|
||||
@ -538,6 +540,6 @@ export function acceptPeerPullPayment(
|
||||
|
||||
export function getTransactionById(tid: string): Promise<Transaction> {
|
||||
return callBackend("getTransactionById", {
|
||||
transactionId: tid
|
||||
})
|
||||
}
|
||||
transactionId: tid,
|
||||
});
|
||||
}
|
||||
|
@ -344,7 +344,6 @@ export async function wxMain(): Promise<void> {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
|
||||
// On platforms that support it, also listen to external
|
||||
// modification of permissions.
|
||||
platform.getPermissionsApi().addPermissionsListener((perm, lastError) => {
|
||||
|
Loading…
Reference in New Issue
Block a user