wxApi from context and using the new testing sdk
This commit is contained in:
parent
8d8d71807d
commit
f93bd51499
@ -9,7 +9,7 @@
|
|||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
||||||
"test": "pnpm compile && mocha 'dist/**/*.test.js' 'dist/**/test.js'",
|
"test": "pnpm compile && mocha --require source-map-support/register 'dist/**/*.test.js' 'dist/**/test.js'",
|
||||||
"test:coverage": "nyc pnpm test",
|
"test:coverage": "nyc pnpm test",
|
||||||
"compile": "tsc && ./build-fast-with-linaria.mjs",
|
"compile": "tsc && ./build-fast-with-linaria.mjs",
|
||||||
"prepare": "pnpm compile",
|
"prepare": "pnpm compile",
|
||||||
|
@ -24,20 +24,20 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { Fragment, h, VNode } from "preact";
|
import { Fragment, h, VNode } from "preact";
|
||||||
|
import { JustInDevMode } from "./components/JustInDevMode.js";
|
||||||
import {
|
import {
|
||||||
NavigationHeader,
|
NavigationHeader,
|
||||||
NavigationHeaderHolder,
|
NavigationHeaderHolder,
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
} from "./components/styled/index.js";
|
} from "./components/styled/index.js";
|
||||||
|
import { useBackendContext } from "./context/backend.js";
|
||||||
import { useTranslationContext } from "./context/translation.js";
|
import { useTranslationContext } from "./context/translation.js";
|
||||||
import settingsIcon from "./svg/settings_black_24dp.svg";
|
|
||||||
import qrIcon from "./svg/qr_code_24px.svg";
|
|
||||||
import warningIcon from "./svg/warning_24px.svg";
|
|
||||||
import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "./wxApi.js";
|
import qrIcon from "./svg/qr_code_24px.svg";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import settingsIcon from "./svg/settings_black_24dp.svg";
|
||||||
import { JustInDevMode } from "./components/JustInDevMode.js";
|
import warningIcon from "./svg/warning_24px.svg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of pages used by the wallet
|
* List of pages used by the wallet
|
||||||
@ -133,13 +133,8 @@ export const Pages = {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PopupNavBar({
|
export function PopupNavBar({ path = "" }: { path?: string }): VNode {
|
||||||
path = "",
|
const api = useBackendContext();
|
||||||
}: {
|
|
||||||
path?: string;
|
|
||||||
}): // api: typeof wxApi,
|
|
||||||
VNode {
|
|
||||||
const api = wxApi; //FIXME: as parameter
|
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
return await api.wallet.call(
|
return await api.wallet.call(
|
||||||
WalletApiOperation.GetUserAttentionUnreadCount,
|
WalletApiOperation.GetUserAttentionUnreadCount,
|
||||||
@ -194,7 +189,7 @@ VNode {
|
|||||||
export function WalletNavBar({ path = "" }: { path?: string }): VNode {
|
export function WalletNavBar({ path = "" }: { path?: string }): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
const api = wxApi; //FIXME: as parameter
|
const api = useBackendContext();
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
return await api.wallet.call(
|
return await api.wallet.call(
|
||||||
WalletApiOperation.GetUserAttentionUnreadCount,
|
WalletApiOperation.GetUserAttentionUnreadCount,
|
||||||
|
@ -22,11 +22,11 @@ import {
|
|||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { Fragment, h, JSX, VNode } from "preact";
|
import { Fragment, h, JSX, VNode } from "preact";
|
||||||
import { useEffect } from "preact/hooks";
|
import { useEffect } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Avatar } from "../mui/Avatar.js";
|
import { Avatar } from "../mui/Avatar.js";
|
||||||
import { Typography } from "../mui/Typography.js";
|
import { Typography } from "../mui/Typography.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
import Banner from "./Banner.js";
|
import Banner from "./Banner.js";
|
||||||
import { Time } from "./Time.js";
|
import { Time } from "./Time.js";
|
||||||
|
|
||||||
@ -35,12 +35,13 @@ interface Props extends JSX.HTMLAttributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PendingTransactions({ goToTransaction }: Props): VNode {
|
export function PendingTransactions({ goToTransaction }: Props): VNode {
|
||||||
|
const api = useBackendContext();
|
||||||
const state = useAsyncAsHook(() =>
|
const state = useAsyncAsHook(() =>
|
||||||
wxApi.wallet.call(WalletApiOperation.GetTransactions, {}),
|
api.wallet.call(WalletApiOperation.GetTransactions, {}),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return wxApi.listener.onUpdateNotification(
|
return api.listener.onUpdateNotification(
|
||||||
[NotificationType.WithdrawGroupFinished],
|
[NotificationType.WithdrawGroupFinished],
|
||||||
state?.retry,
|
state?.retry,
|
||||||
);
|
);
|
||||||
|
@ -25,11 +25,11 @@ import { Loading } from "../components/Loading.js";
|
|||||||
import { LoadingError } from "../components/LoadingError.js";
|
import { LoadingError } from "../components/LoadingError.js";
|
||||||
import { Modal } from "../components/Modal.js";
|
import { Modal } from "../components/Modal.js";
|
||||||
import { Time } from "../components/Time.js";
|
import { Time } from "../components/Time.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../mui/handlers.js";
|
import { ButtonHandler } from "../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../utils/index.js";
|
import { compose, StateViewMap } from "../utils/index.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
import { Amount } from "./Amount.js";
|
import { Amount } from "./Amount.js";
|
||||||
import { Link } from "./styled/index.js";
|
import { Link } from "./styled/index.js";
|
||||||
|
|
||||||
@ -98,7 +98,8 @@ interface Props {
|
|||||||
proposalId: string;
|
proposalId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useComponentState({ proposalId }: Props, api: typeof wxApi): State {
|
function useComponentState({ proposalId }: Props): State {
|
||||||
|
const api = useBackendContext();
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
if (!show) return undefined;
|
if (!show) return undefined;
|
||||||
@ -139,7 +140,7 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const ShowFullContractTermPopup = compose(
|
export const ShowFullContractTermPopup = compose(
|
||||||
"ShowFullContractTermPopup",
|
"ShowFullContractTermPopup",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ToggleHandler } from "../../mui/handlers.js";
|
import { ToggleHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { TermsState } from "./utils.js";
|
import { TermsState } from "./utils.js";
|
||||||
import {
|
import {
|
||||||
@ -26,7 +25,7 @@ import {
|
|||||||
LoadingUriView,
|
LoadingUriView,
|
||||||
ShowButtonsAcceptedTosView,
|
ShowButtonsAcceptedTosView,
|
||||||
ShowButtonsNonAcceptedTosView,
|
ShowButtonsNonAcceptedTosView,
|
||||||
ShowTosContentView,
|
ShowTosContentView
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -89,6 +88,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const TermsOfService = compose(
|
export const TermsOfService = compose(
|
||||||
"TermsOfService",
|
"TermsOfService",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
import { buildTermsOfServiceState } from "./utils.js";
|
import { buildTermsOfServiceState } from "./utils.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ exchangeUrl, onChange }: Props,
|
{ exchangeUrl, onChange }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const readOnly = !onChange;
|
const readOnly = !onChange;
|
||||||
const [showContent, setShowContent] = useState<boolean>(readOnly);
|
const [showContent, setShowContent] = useState<boolean>(readOnly);
|
||||||
const [errorAccepting, setErrorAccepting] = useState<Error | undefined>(
|
const [errorAccepting, setErrorAccepting] = useState<Error | undefined>(
|
||||||
|
53
packages/taler-wallet-webextension/src/context/backend.ts
Normal file
53
packages/taler-wallet-webextension/src/context/backend.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2022 Taler Systems S.A.
|
||||||
|
|
||||||
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentChildren, createContext, h, VNode } from "preact";
|
||||||
|
import { useContext } from "preact/hooks";
|
||||||
|
import { wxApi, WxApiType } from "../wxApi.js";
|
||||||
|
|
||||||
|
type Type = WxApiType
|
||||||
|
|
||||||
|
const initial = wxApi;
|
||||||
|
|
||||||
|
const Context = createContext<Type>(initial);
|
||||||
|
|
||||||
|
type Props = Partial<WxApiType> & {
|
||||||
|
children: ComponentChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BackendProvider = ({
|
||||||
|
wallet,
|
||||||
|
background,
|
||||||
|
listener,
|
||||||
|
children,
|
||||||
|
}: Props): VNode => {
|
||||||
|
|
||||||
|
return h(Context.Provider, {
|
||||||
|
value: {
|
||||||
|
wallet: wallet ?? initial.wallet,
|
||||||
|
background: background ?? initial.background,
|
||||||
|
listener: listener ?? initial.listener
|
||||||
|
},
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBackendContext = (): Type => useContext(Context);
|
@ -19,7 +19,6 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -64,6 +63,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const DepositPage = compose(
|
export const DepositPage = compose(
|
||||||
"Deposit",
|
"Deposit",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
import { Amounts } from "@gnu-taler/taler-util";
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerDepositUri, amountStr, cancel, onSuccess }: Props,
|
{ talerDepositUri, amountStr, cancel, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const info = useAsyncAsHook(async () => {
|
const info = useAsyncAsHook(async () => {
|
||||||
if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
|
if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
|
||||||
if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");
|
if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");
|
||||||
|
@ -20,16 +20,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Amounts } from "@gnu-taler/taler-util";
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { mountHook } from "../../test-utils.js";
|
|
||||||
import { createWalletApiMock } from "../../test-utils.js";
|
import { createWalletApiMock } from "../../test-utils.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
|
import { tests } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
|
import { Props } from "./index.js";
|
||||||
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
describe("Deposit CTA states", () => {
|
describe("Deposit CTA states", () => {
|
||||||
it("should tell the user that the URI is missing", async () => {
|
it("should tell the user that the URI is missing", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
|
||||||
|
const props: Props = {
|
||||||
talerDepositUri: undefined,
|
talerDepositUri: undefined,
|
||||||
amountStr: undefined,
|
amountStr: undefined,
|
||||||
cancel: async () => {
|
cancel: async () => {
|
||||||
@ -39,32 +41,28 @@ describe("Deposit CTA states", () => {
|
|||||||
null;
|
null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const { status } = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
expect(status).equals("loading");
|
expect(status).equals("loading");
|
||||||
}
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading-uri");
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
if (!error) expect.fail();
|
||||||
|
if (!error.hasError) expect.fail();
|
||||||
|
if (error.operational) expect.fail();
|
||||||
|
expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("loading-uri");
|
|
||||||
|
|
||||||
if (!error) expect.fail();
|
|
||||||
if (!error.hasError) expect.fail();
|
|
||||||
if (error.operational) expect.fail();
|
|
||||||
expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
|
|
||||||
}
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ready after loading", async () => {
|
it("should be ready after loading", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PrepareDeposit,
|
WalletApiOperation.PrepareDeposit,
|
||||||
undefined,
|
undefined,
|
||||||
@ -73,6 +71,7 @@ describe("Deposit CTA states", () => {
|
|||||||
totalDepositCost: "EUR:1.2",
|
totalDepositCost: "EUR:1.2",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
talerDepositUri: "payto://refund/asdasdas",
|
talerDepositUri: "payto://refund/asdasdas",
|
||||||
amountStr: "EUR:1",
|
amountStr: "EUR:1",
|
||||||
@ -84,28 +83,21 @@ describe("Deposit CTA states", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.confirm.onClick).not.undefined;
|
||||||
|
expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
|
||||||
|
expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
|
||||||
|
expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ready") expect.fail();
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.confirm.onClick).not.undefined;
|
|
||||||
expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
|
|
||||||
expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
|
|
||||||
expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,6 @@ import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
|
|||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
||||||
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
|
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -78,6 +77,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const InvoiceCreatePage = compose(
|
export const InvoiceCreatePage = compose(
|
||||||
"InvoiceCreatePage",
|
"InvoiceCreatePage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -23,17 +23,17 @@ import {
|
|||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { isFuture, parse } from "date-fns";
|
import { isFuture, parse } from "date-fns";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
|
import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
|
||||||
import { RecursiveState } from "../../utils/index.js";
|
import { RecursiveState } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ amount: amountStr, onClose, onSuccess }: Props,
|
{ amount: amountStr, onClose, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): RecursiveState<State> {
|
): RecursiveState<State> {
|
||||||
const amount = Amounts.parseOrThrow(amountStr);
|
const amount = Amounts.parseOrThrow(amountStr);
|
||||||
|
const api = useBackendContext()
|
||||||
|
|
||||||
const hook = useAsyncAsHook(() =>
|
const hook = useAsyncAsHook(() =>
|
||||||
api.wallet.call(WalletApiOperation.ListExchanges, {}),
|
api.wallet.call(WalletApiOperation.ListExchanges, {}),
|
||||||
@ -158,8 +158,8 @@ export function useComponentState(
|
|||||||
subject === undefined
|
subject === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: !subject
|
: !subject
|
||||||
? "Can't be empty"
|
? "Can't be empty"
|
||||||
: undefined,
|
: undefined,
|
||||||
value: subject ?? "",
|
value: subject ?? "",
|
||||||
onInput: async (e) => setSubject(e),
|
onInput: async (e) => setSubject(e),
|
||||||
},
|
},
|
||||||
|
@ -18,13 +18,12 @@ import {
|
|||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -92,6 +91,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const InvoicePayPage = compose(
|
export const InvoicePayPage = compose(
|
||||||
"InvoicePayPage",
|
"InvoicePayPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -25,14 +25,14 @@ import {
|
|||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props,
|
{ talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, {
|
const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, {
|
||||||
talerUri: talerPayPullUri,
|
talerUri: talerPayPullUri,
|
||||||
|
@ -19,13 +19,12 @@ import {
|
|||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
PreparePayResultAlreadyConfirmed,
|
PreparePayResultAlreadyConfirmed,
|
||||||
PreparePayResultInsufficientBalance,
|
PreparePayResultInsufficientBalance,
|
||||||
PreparePayResultPaymentPossible,
|
PreparePayResultPaymentPossible
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { BaseView, LoadingUriView } from "./views.js";
|
import { BaseView, LoadingUriView } from "./views.js";
|
||||||
|
|
||||||
@ -96,6 +95,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const PaymentPage = compose(
|
export const PaymentPage = compose(
|
||||||
"Payment",
|
"Payment",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -23,16 +23,16 @@ import {
|
|||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props,
|
{ talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined);
|
const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined);
|
||||||
|
const api = useBackendContext()
|
||||||
|
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");
|
if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");
|
||||||
|
@ -30,54 +30,46 @@ import {
|
|||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
import { mountHook, nullFunction } from "../../test-utils.js";
|
import { mountHook, nullFunction } from "../../test-utils.js";
|
||||||
import { createWalletApiMock } from "../../test-utils.js";
|
import { createWalletApiMock } from "../../test-utils.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
|
|
||||||
describe("Payment CTA states", () => {
|
describe("Payment CTA states", () => {
|
||||||
it("should tell the user that the URI is missing", async () => {
|
it("should tell the user that the URI is missing", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: undefined,
|
talerPayUri: undefined,
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const { status, error } = pullLastResultOrThrow();
|
({ status, error }) => {
|
||||||
expect(status).equals("loading");
|
expect(status).equals("loading");
|
||||||
expect(error).undefined;
|
expect(error).undefined;
|
||||||
}
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading-uri");
|
||||||
|
if (error === undefined) expect.fail();
|
||||||
|
expect(error.hasError).true;
|
||||||
|
expect(error.operational).false;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("loading-uri");
|
|
||||||
if (error === undefined) expect.fail();
|
|
||||||
expect(error.hasError).true;
|
|
||||||
expect(error.operational).false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should response with no balance", async () => {
|
it("should response with no balance", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
@ -94,41 +86,34 @@ describe("Payment CTA states", () => {
|
|||||||
{ balances: [] },
|
{ balances: [] },
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "no-balance-for-currency") {
|
||||||
|
expect(state).eq({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(state.balance).undefined;
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "no-balance-for-currency") {
|
|
||||||
expect(r).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(r.balance).undefined;
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be able to pay if there is no enough balance", async () => {
|
it("should not be able to pay if there is no enough balance", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PreparePayForUri,
|
WalletApiOperation.PreparePayForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -153,38 +138,31 @@ describe("Payment CTA states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "no-enough-balance") expect.fail();
|
||||||
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "no-enough-balance") expect.fail();
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to pay (without fee)", async () => {
|
it("should be able to pay (without fee)", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PreparePayForUri,
|
WalletApiOperation.PreparePayForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -209,42 +187,36 @@ describe("Payment CTA states", () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") {
|
||||||
|
expect(state).eq({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
||||||
|
expect(state.payHandler.onClick).not.undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") {
|
|
||||||
expect(r).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
|
|
||||||
expect(r.payHandler.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to pay (with fee)", async () => {
|
it("should be able to pay (with fee)", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PreparePayForUri,
|
WalletApiOperation.PreparePayForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -269,39 +241,32 @@ describe("Payment CTA states", () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
|
expect(state.payHandler.onClick).not.undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") expect.fail();
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
|
||||||
expect(r.payHandler.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get confirmation done after pay successfully", async () => {
|
it("should get confirmation done after pay successfully", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PreparePayForUri,
|
WalletApiOperation.PreparePayForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -332,35 +297,30 @@ describe("Payment CTA states", () => {
|
|||||||
contractTerms: {},
|
contractTerms: {},
|
||||||
} as ConfirmPayResult);
|
} as ConfirmPayResult);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") {
|
||||||
|
expect(state).eq({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
|
if (state.payHandler.onClick === undefined) expect.fail();
|
||||||
|
state.payHandler.onClick();
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") {
|
|
||||||
expect(r).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
|
||||||
if (r.payHandler.onClick === undefined) expect.fail();
|
|
||||||
r.payHandler.onClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not stay in ready state after pay with error", async () => {
|
it("should not stay in ready state after pay with error", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
@ -397,62 +357,50 @@ describe("Payment CTA states", () => {
|
|||||||
lastError: { code: 1 },
|
lastError: { code: 1 },
|
||||||
} as ConfirmPayResult);
|
} as ConfirmPayResult);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
{
|
expect(error).undefined;
|
||||||
const { status, error } = pullLastResultOrThrow();
|
},
|
||||||
expect(status).equals("loading");
|
(state) => {
|
||||||
expect(error).undefined;
|
if (state.status !== "ready") expect.fail();
|
||||||
}
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
expect(await waitForStateUpdate()).true;
|
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||||
|
if (state.payHandler.onClick === undefined) expect.fail();
|
||||||
{
|
state.payHandler.onClick();
|
||||||
const r = pullLastResultOrThrow();
|
},
|
||||||
if (r.status !== "ready") expect.fail();
|
(state) => {
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
if (state.status !== "ready") expect.fail();
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
if (r.payHandler.onClick === undefined) expect.fail();
|
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||||
r.payHandler.onClick();
|
expect(state.payHandler.onClick).undefined;
|
||||||
}
|
if (state.payHandler.error === undefined) expect.fail();
|
||||||
|
//FIXME: error message here is bad
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state.payHandler.error.errorDetail.hint).eq(
|
||||||
|
"could not confirm payment",
|
||||||
{
|
);
|
||||||
const r = pullLastResultOrThrow();
|
expect(state.payHandler.error.errorDetail.payResult).deep.equal({
|
||||||
if (r.status !== "ready") expect.fail();
|
type: ConfirmPayResultType.Pending,
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
lastError: { code: 1 },
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
});
|
||||||
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
},
|
||||||
expect(r.payHandler.onClick).undefined;
|
], TestingContext)
|
||||||
if (r.payHandler.error === undefined) expect.fail();
|
|
||||||
//FIXME: error message here is bad
|
|
||||||
expect(r.payHandler.error.errorDetail.hint).eq(
|
|
||||||
"could not confirm payment",
|
|
||||||
);
|
|
||||||
expect(r.payHandler.error.errorDetail.payResult).deep.equal({
|
|
||||||
type: ConfirmPayResultType.Pending,
|
|
||||||
lastError: { code: 1 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
|
|
||||||
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update balance if a coins is withdraw", async () => {
|
it("should update balance if a coins is withdraw", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
talerPayUri: "taller://pay",
|
talerPayUri: "taller://pay",
|
||||||
cancel: nullFunction,
|
cancel: nullFunction,
|
||||||
goToWalletManualWithdraw: nullFunction,
|
goToWalletManualWithdraw: nullFunction,
|
||||||
onSuccess: async () => {
|
onSuccess: nullFunction,
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
@ -507,46 +455,30 @@ describe("Payment CTA states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail()
|
||||||
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
|
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||||
|
expect(state.payHandler.onClick).not.undefined;
|
||||||
|
|
||||||
{
|
handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
|
||||||
const { status, error } = pullLastResultOrThrow();
|
},
|
||||||
expect(status).equals("loading");
|
(state) => {
|
||||||
expect(error).undefined;
|
if (state.status !== "ready") expect.fail()
|
||||||
}
|
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||||
|
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||||
|
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||||
|
expect(state.payHandler.onClick).not.undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") {
|
|
||||||
expect(r).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
|
||||||
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
|
||||||
expect(r.payHandler.onClick).not.undefined;
|
|
||||||
|
|
||||||
handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") {
|
|
||||||
expect(r).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
|
||||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
|
||||||
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
|
||||||
expect(r.payHandler.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,6 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -60,6 +59,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const RecoveryPage = compose(
|
export const RecoveryPage = compose(
|
||||||
"Recovery",
|
"Recovery",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
import { parseRecoveryUri } from "@gnu-taler/taler-util";
|
import { parseRecoveryUri } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { wxApi } from "../../wxApi.js";
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerRecoveryUri, onCancel, onSuccess }: Props,
|
{ talerRecoveryUri, onCancel, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
if (!talerRecoveryUri) {
|
if (!talerRecoveryUri) {
|
||||||
return {
|
return {
|
||||||
status: "loading-uri",
|
status: "loading-uri",
|
||||||
@ -48,7 +48,7 @@ export function useComponentState(
|
|||||||
const recovery = info;
|
const recovery = info;
|
||||||
|
|
||||||
async function recoverBackup(): Promise<void> {
|
async function recoverBackup(): Promise<void> {
|
||||||
await wxApi.wallet.call(WalletApiOperation.ImportBackupRecovery, {
|
await api.wallet.call(WalletApiOperation.ImportBackupRecovery, {
|
||||||
recovery,
|
recovery,
|
||||||
});
|
});
|
||||||
onSuccess();
|
onSuccess();
|
||||||
|
@ -19,13 +19,12 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import {
|
import {
|
||||||
IgnoredView,
|
IgnoredView,
|
||||||
InProgressView,
|
InProgressView,
|
||||||
LoadingUriView,
|
LoadingUriView,
|
||||||
ReadyView,
|
ReadyView
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -90,6 +89,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const RefundPage = compose(
|
export const RefundPage = compose(
|
||||||
"Refund",
|
"Refund",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
import { Amounts, NotificationType } from "@gnu-taler/taler-util";
|
import { Amounts, NotificationType } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerRefundUri, cancel, onSuccess }: Props,
|
{ talerRefundUri, cancel, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const [ignored, setIgnored] = useState(false);
|
const [ignored, setIgnored] = useState(false);
|
||||||
|
|
||||||
const info = useAsyncAsHook(async () => {
|
const info = useAsyncAsHook(async () => {
|
||||||
|
@ -20,75 +20,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
Amounts,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
OrderShortInfo,
|
OrderShortInfo
|
||||||
PrepareRefundResult,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { mountHook } from "../../test-utils.js";
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
import { createWalletApiMock } from "../../test-utils.js";
|
import { createWalletApiMock, mountHook, nullFunction } from "../../test-utils.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
|
|
||||||
describe("Refund CTA states", () => {
|
describe("Refund CTA states", () => {
|
||||||
it("should tell the user that the URI is missing", async () => {
|
it("should tell the user that the URI is missing", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const props = {
|
||||||
mountHook(() =>
|
talerRefundUri: undefined,
|
||||||
useComponentState(
|
cancel: nullFunction,
|
||||||
{
|
onSuccess: nullFunction,
|
||||||
talerRefundUri: undefined,
|
|
||||||
cancel: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mock,
|
|
||||||
// {
|
|
||||||
// prepareRefund: async () => ({}),
|
|
||||||
// applyRefund: async () => ({}),
|
|
||||||
// onUpdateNotification: async () => ({}),
|
|
||||||
// } as any,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading-uri");
|
||||||
|
if (!error) expect.fail();
|
||||||
|
if (!error.hasError) expect.fail();
|
||||||
|
if (error.operational) expect.fail();
|
||||||
|
expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("loading-uri");
|
|
||||||
if (!error) expect.fail();
|
|
||||||
if (!error.hasError) expect.fail();
|
|
||||||
if (error.operational) expect.fail();
|
|
||||||
expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ready after loading", async () => {
|
it("should be ready after loading", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerRefundUri: "taler://refund/asdasdas",
|
talerRefundUri: "taler://refund/asdasdas",
|
||||||
cancel: async () => {
|
cancel: nullFunction,
|
||||||
null;
|
onSuccess: nullFunction,
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareRefund, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareRefund, undefined, {
|
||||||
@ -108,61 +83,28 @@ describe("Refund CTA states", () => {
|
|||||||
} as OrderShortInfo,
|
} as OrderShortInfo,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() =>
|
({ status, error }) => {
|
||||||
useComponentState(
|
expect(status).equals("loading");
|
||||||
props,
|
expect(error).undefined;
|
||||||
mock,
|
},
|
||||||
// {
|
(state) => {
|
||||||
// prepareRefund: async () =>
|
if (state.status !== "ready") expect.fail();
|
||||||
// ({
|
if (state.error) expect.fail();
|
||||||
// effectivePaid: "EUR:2",
|
expect(state.accept.onClick).not.undefined;
|
||||||
// awaiting: "EUR:2",
|
expect(state.ignore.onClick).not.undefined;
|
||||||
// gone: "EUR:0",
|
expect(state.merchantName).eq("the merchant name");
|
||||||
// granted: "EUR:0",
|
expect(state.orderId).eq("orderId1");
|
||||||
// pending: false,
|
expect(state.products).undefined;
|
||||||
// proposalId: "1",
|
},
|
||||||
// info: {
|
], TestingContext)
|
||||||
// contractTermsHash: "123",
|
|
||||||
// merchant: {
|
|
||||||
// name: "the merchant name",
|
|
||||||
// },
|
|
||||||
// orderId: "orderId1",
|
|
||||||
// summary: "the summary",
|
|
||||||
// },
|
|
||||||
// } as PrepareRefundResult as any),
|
|
||||||
// applyRefund: async () => ({}),
|
|
||||||
// onUpdateNotification: async () => ({}),
|
|
||||||
// } as any,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ready") expect.fail();
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.accept.onClick).not.undefined;
|
|
||||||
expect(state.ignore.onClick).not.undefined;
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
expect(state.orderId).eq("orderId1");
|
|
||||||
expect(state.products).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ignored after clicking the ignore button", async () => {
|
it("should be ignored after clicking the ignore button", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerRefundUri: "taler://refund/asdasdas",
|
talerRefundUri: "taler://refund/asdasdas",
|
||||||
cancel: async () => {
|
cancel: async () => {
|
||||||
@ -189,102 +131,36 @@ describe("Refund CTA states", () => {
|
|||||||
summary: "the summary",
|
summary: "the summary",
|
||||||
} as OrderShortInfo,
|
} as OrderShortInfo,
|
||||||
});
|
});
|
||||||
// handler.addWalletCall(WalletApiOperation.ApplyRefund)
|
|
||||||
// handler.addWalletCall(WalletApiOperation.PrepareRefund, undefined, {
|
|
||||||
// awaiting: "EUR:1",
|
|
||||||
// effectivePaid: "EUR:2",
|
|
||||||
// gone: "EUR:0",
|
|
||||||
// granted: "EUR:1",
|
|
||||||
// pending: true,
|
|
||||||
// proposalId: "1",
|
|
||||||
// info: {
|
|
||||||
// contractTermsHash: "123",
|
|
||||||
// merchant: {
|
|
||||||
// name: "the merchant name",
|
|
||||||
// },
|
|
||||||
// orderId: "orderId1",
|
|
||||||
// summary: "the summary",
|
|
||||||
// } as OrderShortInfo,
|
|
||||||
// })
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() =>
|
|
||||||
useComponentState(
|
|
||||||
props,
|
|
||||||
mock,
|
|
||||||
// {
|
|
||||||
// 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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const { status, error } = pullLastResultOrThrow();
|
({ status, error }) => {
|
||||||
expect(status).equals("loading");
|
expect(status).equals("loading");
|
||||||
expect(error).undefined;
|
expect(error).undefined;
|
||||||
}
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail()
|
||||||
|
if (state.error) expect.fail()
|
||||||
|
expect(state.accept.onClick).not.undefined;
|
||||||
|
expect(state.merchantName).eq("the merchant name");
|
||||||
|
expect(state.orderId).eq("orderId1");
|
||||||
|
expect(state.products).undefined;
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
if (state.ignore.onClick === undefined) expect.fail();
|
||||||
|
state.ignore.onClick();
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ignored") expect.fail()
|
||||||
|
if (state.error) expect.fail()
|
||||||
|
expect(state.merchantName).eq("the merchant name");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ready") {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(state.accept.onClick).not.undefined;
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
expect(state.orderId).eq("orderId1");
|
|
||||||
expect(state.products).undefined;
|
|
||||||
|
|
||||||
if (state.ignore.onClick === undefined) expect.fail();
|
|
||||||
state.ignore.onClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ignored") {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be in progress when doing refresh", async () => {
|
it("should be in progress when doing refresh", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerRefundUri: "taler://refund/asdasdas",
|
talerRefundUri: "taler://refund/asdasdas",
|
||||||
cancel: async () => {
|
cancel: async () => {
|
||||||
@ -344,67 +220,42 @@ describe("Refund CTA states", () => {
|
|||||||
} as OrderShortInfo,
|
} as OrderShortInfo,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "in-progress") expect.fail()
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.merchantName).eq("the merchant name");
|
||||||
|
expect(state.products).undefined;
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
||||||
|
// expect(state.progress).closeTo(1 / 3, 0.01)
|
||||||
|
|
||||||
{
|
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
|
||||||
const { status, error } = pullLastResultOrThrow();
|
},
|
||||||
expect(status).equals("loading");
|
(state) => {
|
||||||
expect(error).undefined;
|
if (state.status !== "in-progress") expect.fail()
|
||||||
}
|
if (state.error) expect.fail();
|
||||||
|
expect(state.merchantName).eq("the merchant name");
|
||||||
|
expect(state.products).undefined;
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
||||||
|
// expect(state.progress).closeTo(2 / 3, 0.01)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail()
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.merchantName).eq("the merchant name");
|
||||||
|
expect(state.products).undefined;
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
||||||
|
|
||||||
{
|
},
|
||||||
const state = pullLastResultOrThrow();
|
], TestingContext)
|
||||||
|
|
||||||
if (state.status !== "in-progress") {
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
expect(state.products).undefined;
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
|
||||||
// expect(state.progress).closeTo(1 / 3, 0.01)
|
|
||||||
|
|
||||||
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "in-progress") {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
expect(state.products).undefined;
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
|
||||||
// expect(state.progress).closeTo(2 / 3, 0.01)
|
|
||||||
|
|
||||||
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ready") {
|
|
||||||
expect(state).eq({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.merchantName).eq("the merchant name");
|
|
||||||
expect(state.products).undefined;
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,13 +19,12 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import {
|
import {
|
||||||
AcceptedView,
|
AcceptedView,
|
||||||
IgnoredView,
|
IgnoredView,
|
||||||
LoadingUriView,
|
LoadingUriView,
|
||||||
ReadyView,
|
ReadyView
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -84,6 +83,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const TipPage = compose(
|
export const TipPage = compose(
|
||||||
"Tip",
|
"Tip",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
import { Amounts } from "@gnu-taler/taler-util";
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerTipUri, onCancel, onSuccess }: Props,
|
{ talerTipUri, onCancel, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const tipInfo = useAsyncAsHook(async () => {
|
const tipInfo = useAsyncAsHook(async () => {
|
||||||
if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
|
if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
|
||||||
const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
|
const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
|
||||||
|
@ -22,54 +22,42 @@
|
|||||||
import { Amounts } from "@gnu-taler/taler-util";
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { mountHook } from "../../test-utils.js";
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
|
import { mountHook, nullFunction } from "../../test-utils.js";
|
||||||
import { createWalletApiMock } from "../../test-utils.js";
|
import { createWalletApiMock } from "../../test-utils.js";
|
||||||
|
import { Props } from "./index.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
|
|
||||||
describe("Tip CTA states", () => {
|
describe("Tip CTA states", () => {
|
||||||
it("should tell the user that the URI is missing", async () => {
|
it("should tell the user that the URI is missing", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const props: Props = {
|
||||||
mountHook(() =>
|
talerTipUri: undefined,
|
||||||
useComponentState(
|
onCancel: nullFunction,
|
||||||
{
|
onSuccess: nullFunction,
|
||||||
talerTipUri: undefined,
|
|
||||||
onCancel: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mock,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading-uri");
|
||||||
|
if (!error) expect.fail();
|
||||||
|
if (!error.hasError) expect.fail();
|
||||||
|
if (error.operational) expect.fail();
|
||||||
|
expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("loading-uri");
|
|
||||||
if (!error) expect.fail();
|
|
||||||
if (!error.hasError) expect.fail();
|
|
||||||
if (error.operational) expect.fail();
|
|
||||||
expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ready for accepting the tip", async () => {
|
it("should be ready for accepting the tip", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
||||||
accepted: false,
|
accepted: false,
|
||||||
@ -83,78 +71,59 @@ describe("Tip CTA states", () => {
|
|||||||
tipAmountRaw: "",
|
tipAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const props: Props = {
|
||||||
mountHook(() =>
|
talerTipUri: "taler://tip/asd",
|
||||||
useComponentState(
|
onCancel: nullFunction,
|
||||||
{
|
onSuccess: nullFunction,
|
||||||
talerTipUri: "taler://tip/asd",
|
|
||||||
onCancel: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mock,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
|
({ status, error }) => {
|
||||||
{
|
expect(status).equals("loading");
|
||||||
const state = pullLastResultOrThrow();
|
expect(error).undefined;
|
||||||
|
|
||||||
if (state.status !== "ready") {
|
|
||||||
expect(state).eq({ status: "ready" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
|
||||||
expect(state.merchantBaseUrl).eq("merchant url");
|
|
||||||
expect(state.exchangeBaseUrl).eq("exchange url");
|
|
||||||
if (state.accept.onClick === undefined) expect.fail();
|
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
|
|
||||||
state.accept.onClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
|
||||||
accepted: true,
|
|
||||||
exchangeBaseUrl: "exchange url",
|
|
||||||
merchantBaseUrl: "merchant url",
|
|
||||||
tipAmountEffective: "EUR:1",
|
|
||||||
walletTipId: "tip_id",
|
|
||||||
expirationTimestamp: {
|
|
||||||
t_s: 1,
|
|
||||||
},
|
},
|
||||||
tipAmountRaw: "",
|
(state) => {
|
||||||
});
|
if (state.status !== "ready") {
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state).eq({ status: "ready" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
||||||
|
expect(state.merchantBaseUrl).eq("merchant url");
|
||||||
|
expect(state.exchangeBaseUrl).eq("exchange url");
|
||||||
|
if (state.accept.onClick === undefined) expect.fail();
|
||||||
|
|
||||||
{
|
handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
|
||||||
const state = pullLastResultOrThrow();
|
state.accept.onClick();
|
||||||
|
|
||||||
if (state.status !== "accepted") {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
||||||
expect(state).eq({ status: "accepted" });
|
accepted: true,
|
||||||
return;
|
exchangeBaseUrl: "exchange url",
|
||||||
}
|
merchantBaseUrl: "merchant url",
|
||||||
if (state.error) expect.fail();
|
tipAmountEffective: "EUR:1",
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
walletTipId: "tip_id",
|
||||||
expect(state.merchantBaseUrl).eq("merchant url");
|
expirationTimestamp: {
|
||||||
expect(state.exchangeBaseUrl).eq("exchange url");
|
t_s: 1,
|
||||||
}
|
},
|
||||||
await assertNoPendingUpdate();
|
tipAmountRaw: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "accepted") expect.fail()
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
||||||
|
expect(state.merchantBaseUrl).eq("merchant url");
|
||||||
|
expect(state.exchangeBaseUrl).eq("exchange url");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ignored after clicking the ignore button", async () => {
|
it.skip("should be ignored after clicking the ignore button", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
||||||
exchangeBaseUrl: "exchange url",
|
exchangeBaseUrl: "exchange url",
|
||||||
merchantBaseUrl: "merchant url",
|
merchantBaseUrl: "merchant url",
|
||||||
@ -167,46 +136,34 @@ describe("Tip CTA states", () => {
|
|||||||
tipAmountRaw: "",
|
tipAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const props: Props = {
|
||||||
mountHook(() =>
|
talerTipUri: "taler://tip/asd",
|
||||||
useComponentState(
|
onCancel: nullFunction,
|
||||||
{
|
onSuccess: nullFunction,
|
||||||
talerTipUri: "taler://tip/asd",
|
|
||||||
onCancel: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mock,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
||||||
|
expect(state.merchantBaseUrl).eq("merchant url");
|
||||||
|
expect(state.exchangeBaseUrl).eq("exchange url");
|
||||||
|
|
||||||
{
|
//FIXME: add ignore button
|
||||||
const state = pullLastResultOrThrow();
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
if (state.status !== "ready") expect.fail();
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
|
||||||
expect(state.merchantBaseUrl).eq("merchant url");
|
|
||||||
expect(state.exchangeBaseUrl).eq("exchange url");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render accepted if the tip has been used previously", async () => {
|
it("should render accepted if the tip has been used previously", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
||||||
accepted: true,
|
accepted: true,
|
||||||
@ -220,40 +177,28 @@ describe("Tip CTA states", () => {
|
|||||||
tipAmountRaw: "",
|
tipAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const props: Props = {
|
||||||
mountHook(() =>
|
talerTipUri: "taler://tip/asd",
|
||||||
useComponentState(
|
onCancel: nullFunction,
|
||||||
{
|
onSuccess: nullFunction,
|
||||||
talerTipUri: "taler://tip/asd",
|
|
||||||
onCancel: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mock,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading");
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "accepted") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
||||||
|
expect(state.merchantBaseUrl).eq("merchant url");
|
||||||
|
expect(state.exchangeBaseUrl).eq("exchange url");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "accepted") expect.fail();
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
|
|
||||||
expect(state.merchantBaseUrl).eq("merchant url");
|
|
||||||
expect(state.exchangeBaseUrl).eq("exchange url");
|
|
||||||
}
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,6 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
|
import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -66,6 +65,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const TransferCreatePage = compose(
|
export const TransferCreatePage = compose(
|
||||||
"TransferCreatePage",
|
"TransferCreatePage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,19 +17,19 @@
|
|||||||
import {
|
import {
|
||||||
Amounts,
|
Amounts,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { format, isFuture, parse } from "date-fns";
|
import { isFuture, parse } from "date-fns";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ amount: amountStr, onClose, onSuccess }: Props,
|
{ amount: amountStr, onClose, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const amount = Amounts.parseOrThrow(amountStr);
|
const amount = Amounts.parseOrThrow(amountStr);
|
||||||
|
|
||||||
const [subject, setSubject] = useState<string | undefined>();
|
const [subject, setSubject] = useState<string | undefined>();
|
||||||
@ -124,8 +124,8 @@ export function useComponentState(
|
|||||||
subject === undefined
|
subject === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: !subject
|
: !subject
|
||||||
? "Can't be empty"
|
? "Can't be empty"
|
||||||
: undefined,
|
: undefined,
|
||||||
value: subject ?? "",
|
value: subject ?? "",
|
||||||
onInput: async (e) => setSubject(e),
|
onInput: async (e) => setSubject(e),
|
||||||
},
|
},
|
||||||
|
@ -17,13 +17,12 @@
|
|||||||
import {
|
import {
|
||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { ButtonHandler } from "../../mui/handlers.js";
|
import { ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -69,6 +68,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const TransferPickupPage = compose(
|
export const TransferPickupPage = compose(
|
||||||
"TransferPickupPage",
|
"TransferPickupPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -18,18 +18,18 @@ import {
|
|||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
Amounts,
|
Amounts,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ talerPayPushUri, onClose, onSuccess }: Props,
|
{ talerPayPushUri, onClose, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
|
return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
|
||||||
talerUri: talerPayPushUri,
|
talerUri: talerPayPushUri,
|
||||||
|
@ -20,10 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
|||||||
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
||||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import {
|
import {
|
||||||
useComponentStateFromParams,
|
useComponentStateFromParams,
|
||||||
useComponentStateFromURI,
|
useComponentStateFromURI
|
||||||
} from "./state.js";
|
} from "./state.js";
|
||||||
|
|
||||||
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
||||||
@ -96,11 +95,11 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const WithdrawPageFromURI = compose(
|
export const WithdrawPageFromURI = compose(
|
||||||
"WithdrawPageFromURI",
|
"WithdrawPageFromURI",
|
||||||
(p: PropsFromURI) => useComponentStateFromURI(p, wxApi),
|
(p: PropsFromURI) => useComponentStateFromURI(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
export const WithdrawPageFromParams = compose(
|
export const WithdrawPageFromParams = compose(
|
||||||
"WithdrawPageFromParams",
|
"WithdrawPageFromParams",
|
||||||
(p: PropsFromParams) => useComponentStateFromParams(p, wxApi),
|
(p: PropsFromParams) => useComponentStateFromParams(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -19,20 +19,20 @@ import {
|
|||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangeTosStatus,
|
ExchangeTosStatus
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
|
import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
|
||||||
import { RecursiveState } from "../../utils/index.js";
|
import { RecursiveState } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { PropsFromParams, PropsFromURI, State } from "./index.js";
|
import { PropsFromParams, PropsFromURI, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentStateFromParams(
|
export function useComponentStateFromParams(
|
||||||
{ amount, cancel, onSuccess }: PropsFromParams,
|
{ amount, cancel, onSuccess }: PropsFromParams,
|
||||||
api: typeof wxApi,
|
|
||||||
): RecursiveState<State> {
|
): RecursiveState<State> {
|
||||||
|
const api = useBackendContext()
|
||||||
const uriInfoHook = useAsyncAsHook(async () => {
|
const uriInfoHook = useAsyncAsHook(async () => {
|
||||||
const exchanges = await api.wallet.call(
|
const exchanges = await api.wallet.call(
|
||||||
WalletApiOperation.ListExchanges,
|
WalletApiOperation.ListExchanges,
|
||||||
@ -84,14 +84,13 @@ export function useComponentStateFromParams(
|
|||||||
chosenAmount,
|
chosenAmount,
|
||||||
exchangeList,
|
exchangeList,
|
||||||
undefined,
|
undefined,
|
||||||
api,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useComponentStateFromURI(
|
export function useComponentStateFromURI(
|
||||||
{ talerWithdrawUri, cancel, onSuccess }: PropsFromURI,
|
{ talerWithdrawUri, cancel, onSuccess }: PropsFromURI,
|
||||||
api: typeof wxApi,
|
|
||||||
): RecursiveState<State> {
|
): RecursiveState<State> {
|
||||||
|
const api = useBackendContext()
|
||||||
/**
|
/**
|
||||||
* Ask the wallet about the withdraw URI
|
* Ask the wallet about the withdraw URI
|
||||||
*/
|
*/
|
||||||
@ -158,7 +157,6 @@ export function useComponentStateFromURI(
|
|||||||
chosenAmount,
|
chosenAmount,
|
||||||
exchangeList,
|
exchangeList,
|
||||||
defaultExchange,
|
defaultExchange,
|
||||||
api,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +174,8 @@ function exchangeSelectionState(
|
|||||||
chosenAmount: AmountJson,
|
chosenAmount: AmountJson,
|
||||||
exchangeList: ExchangeListItem[],
|
exchangeList: ExchangeListItem[],
|
||||||
defaultExchange: string | undefined,
|
defaultExchange: string | undefined,
|
||||||
api: typeof wxApi,
|
|
||||||
): RecursiveState<State> {
|
): RecursiveState<State> {
|
||||||
|
const api = useBackendContext()
|
||||||
const selectedExchange = useSelectedExchange({
|
const selectedExchange = useSelectedExchange({
|
||||||
currency: chosenAmount.currency,
|
currency: chosenAmount.currency,
|
||||||
defaultExchange,
|
defaultExchange,
|
||||||
@ -278,10 +276,10 @@ function exchangeSelectionState(
|
|||||||
//TODO: calculate based on exchange info
|
//TODO: calculate based on exchange info
|
||||||
const ageRestriction = ageRestrictionEnabled
|
const ageRestriction = ageRestrictionEnabled
|
||||||
? {
|
? {
|
||||||
list: ageRestrictionOptions,
|
list: ageRestrictionOptions,
|
||||||
value: String(ageRestricted),
|
value: String(ageRestricted),
|
||||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
import { mountHook } from "../../test-utils.js";
|
import { mountHook } from "../../test-utils.js";
|
||||||
import { createWalletApiMock } from "../../test-utils.js";
|
import { createWalletApiMock } from "../../test-utils.js";
|
||||||
import { useComponentStateFromURI } from "./state.js";
|
import { useComponentStateFromURI } from "./state.js";
|
||||||
@ -61,53 +62,45 @@ const exchanges: ExchangeListItem[] = [
|
|||||||
} as Partial<ExchangeListItem> as ExchangeListItem,
|
} as Partial<ExchangeListItem> as ExchangeListItem,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const nullFunction = async (): Promise<void> => {
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
describe("Withdraw CTA states", () => {
|
describe("Withdraw CTA states", () => {
|
||||||
it("should tell the user that the URI is missing", async () => {
|
it("should tell the user that the URI is missing", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
talerWithdrawUri: undefined,
|
talerWithdrawUri: undefined,
|
||||||
cancel: async () => {
|
cancel: nullFunction,
|
||||||
null;
|
onSuccess: nullFunction,
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentStateFromURI(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
|
||||||
const { status } = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
expect(status).equals("loading");
|
expect(status).equals("loading");
|
||||||
}
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
if (status != "uri-error") expect.fail();
|
||||||
|
if (!error) expect.fail();
|
||||||
|
if (!error.hasError) expect.fail();
|
||||||
|
if (error.operational) expect.fail();
|
||||||
|
expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (status != "uri-error") expect.fail();
|
|
||||||
if (!error) expect.fail();
|
|
||||||
if (!error.hasError) expect.fail();
|
|
||||||
if (error.operational) expect.fail();
|
|
||||||
expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should tell the user that there is not known exchange", async () => {
|
it("should tell the user that there is not known exchange", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerWithdrawUri: "taler-withdraw://",
|
talerWithdrawUri: "taler-withdraw://",
|
||||||
cancel: async () => {
|
cancel: nullFunction,
|
||||||
null;
|
onSuccess: nullFunction,
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.GetWithdrawalDetailsForUri,
|
WalletApiOperation.GetWithdrawalDetailsForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -117,39 +110,28 @@ describe("Withdraw CTA states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
|
||||||
mountHook(() => useComponentStateFromURI(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("no-exchange");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equals("loading", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("no-exchange", "3");
|
|
||||||
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to withdraw if tos are ok", async () => {
|
it("should be able to withdraw if tos are ok", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerWithdrawUri: "taler-withdraw://",
|
talerWithdrawUri: "taler-withdraw://",
|
||||||
cancel: async () => {
|
cancel: nullFunction,
|
||||||
null;
|
onSuccess: nullFunction,
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.GetWithdrawalDetailsForUri,
|
WalletApiOperation.GetWithdrawalDetailsForUri,
|
||||||
undefined,
|
undefined,
|
||||||
@ -171,54 +153,38 @@ describe("Withdraw CTA states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
|
||||||
mountHook(() => useComponentStateFromURI(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
expect(state.status).equals("success");
|
||||||
|
if (state.status !== "success") return;
|
||||||
|
|
||||||
{
|
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
const { status, error } = pullLastResultOrThrow();
|
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
||||||
expect(status).equals("loading");
|
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state.doWithdrawal.onClick).not.undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status, error } = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
expect(status).equals("loading");
|
|
||||||
|
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
expect(state.status).equals("success");
|
|
||||||
if (state.status !== "success") return;
|
|
||||||
|
|
||||||
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
|
||||||
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
|
|
||||||
expect(state.doWithdrawal.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should accept the tos before withdraw", async () => {
|
it("should accept the tos before withdraw", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = {
|
const props = {
|
||||||
talerWithdrawUri: "taler-withdraw://",
|
talerWithdrawUri: "taler-withdraw://",
|
||||||
cancel: async () => {
|
cancel: nullFunction,
|
||||||
null;
|
onSuccess: nullFunction,
|
||||||
},
|
|
||||||
onSuccess: async () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const exchangeWithNewTos = exchanges.map((e) => ({
|
const exchangeWithNewTos = exchanges.map((e) => ({
|
||||||
...e,
|
...e,
|
||||||
tosStatus: ExchangeTosStatus.New,
|
tosStatus: ExchangeTosStatus.New,
|
||||||
@ -255,57 +221,39 @@ describe("Withdraw CTA states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
|
||||||
mountHook(() => useComponentStateFromURI(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
},
|
||||||
|
({ status, error }) => {
|
||||||
|
expect(status).equals("loading");
|
||||||
|
expect(error).undefined;
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
expect(state.status).equals("success");
|
||||||
|
if (state.status !== "success") return;
|
||||||
|
|
||||||
{
|
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
const { status, error } = pullLastResultOrThrow();
|
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
||||||
expect(status).equals("loading");
|
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
expect(error).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state.doWithdrawal.onClick).undefined;
|
||||||
|
|
||||||
{
|
state.onTosUpdate();
|
||||||
const { status, error } = pullLastResultOrThrow();
|
},
|
||||||
|
(state) => {
|
||||||
|
expect(state.status).equals("success");
|
||||||
|
if (state.status !== "success") return;
|
||||||
|
|
||||||
expect(status).equals("loading");
|
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
|
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
||||||
|
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
||||||
|
|
||||||
expect(error).undefined;
|
expect(state.doWithdrawal.onClick).not.undefined;
|
||||||
}
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
expect(state.status).equals("success");
|
|
||||||
if (state.status !== "success") return;
|
|
||||||
|
|
||||||
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
|
||||||
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
|
|
||||||
expect(state.doWithdrawal.onClick).undefined;
|
|
||||||
|
|
||||||
// updateAcceptedVersionToCurrentVersion();
|
|
||||||
state.onTosUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
expect(state.status).equals("success");
|
|
||||||
if (state.status !== "success") return;
|
|
||||||
|
|
||||||
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
|
|
||||||
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
|
|
||||||
|
|
||||||
expect(state.doWithdrawal.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,22 +16,23 @@
|
|||||||
|
|
||||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { ToggleHandler } from "../mui/handlers.js";
|
import { ToggleHandler } from "../mui/handlers.js";
|
||||||
import { platform } from "../platform/api.js";
|
import { platform } from "../platform/api.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
export function useAutoOpenPermissions(): ToggleHandler {
|
export function useAutoOpenPermissions(): ToggleHandler {
|
||||||
|
const api = useBackendContext();
|
||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
const [error, setError] = useState<TalerError | undefined>();
|
const [error, setError] = useState<TalerError | undefined>();
|
||||||
const toggle = async (): Promise<void> => {
|
const toggle = async (): Promise<void> => {
|
||||||
return handleAutoOpenPerm(enabled, setEnabled).catch((e) => {
|
return handleAutoOpenPerm(enabled, setEnabled, api.background).catch((e) => {
|
||||||
setError(TalerError.fromException(e));
|
setError(TalerError.fromException(e));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getValue(): Promise<void> {
|
async function getValue(): Promise<void> {
|
||||||
const res = await wxApi.background.containsHeaderListener();
|
const res = await api.background.containsHeaderListener();
|
||||||
setEnabled(res.newValue);
|
setEnabled(res.newValue);
|
||||||
}
|
}
|
||||||
getValue();
|
getValue();
|
||||||
@ -48,6 +49,7 @@ export function useAutoOpenPermissions(): ToggleHandler {
|
|||||||
async function handleAutoOpenPerm(
|
async function handleAutoOpenPerm(
|
||||||
isEnabled: boolean,
|
isEnabled: boolean,
|
||||||
onChange: (value: boolean) => void,
|
onChange: (value: boolean) => void,
|
||||||
|
background: ReturnType<typeof useBackendContext>["background"],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
// We set permissions here, since apparently FF wants this to be done
|
// We set permissions here, since apparently FF wants this to be done
|
||||||
@ -59,11 +61,11 @@ async function handleAutoOpenPerm(
|
|||||||
onChange(false);
|
onChange(false);
|
||||||
throw lastError;
|
throw lastError;
|
||||||
}
|
}
|
||||||
const res = await wxApi.background.toggleHeaderListener(granted);
|
const res = await background.toggleHeaderListener(granted);
|
||||||
onChange(res.newValue);
|
onChange(res.newValue);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await wxApi.background
|
await background
|
||||||
.toggleHeaderListener(false)
|
.toggleHeaderListener(false)
|
||||||
.then((r) => onChange(r.newValue));
|
.then((r) => onChange(r.newValue));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { wxApi } from "../wxApi.js";
|
import { useBackendContext } from "../context/backend.js";
|
||||||
|
|
||||||
export interface BackupDeviceName {
|
export interface BackupDeviceName {
|
||||||
name: string;
|
name: string;
|
||||||
@ -28,17 +28,18 @@ export function useBackupDeviceName(): BackupDeviceName {
|
|||||||
name: "",
|
name: "",
|
||||||
update: () => Promise.resolve(),
|
update: () => Promise.resolve(),
|
||||||
});
|
});
|
||||||
|
const api = useBackendContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
//create a first list of backup info by currency
|
//create a first list of backup info by currency
|
||||||
const status = await wxApi.wallet.call(
|
const status = await api.wallet.call(
|
||||||
WalletApiOperation.GetBackupInfo,
|
WalletApiOperation.GetBackupInfo,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
async function update(newName: string): Promise<void> {
|
async function update(newName: string): Promise<void> {
|
||||||
await wxApi.wallet.call(WalletApiOperation.SetWalletDeviceId, {
|
await api.wallet.call(WalletApiOperation.SetWalletDeviceId, {
|
||||||
walletDeviceId: newName,
|
walletDeviceId: newName,
|
||||||
});
|
});
|
||||||
setStatus((old) => ({ ...old, name: newName }));
|
setStatus((old) => ({ ...old, name: newName }));
|
||||||
|
@ -16,22 +16,24 @@
|
|||||||
|
|
||||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { ToggleHandler } from "../mui/handlers.js";
|
import { ToggleHandler } from "../mui/handlers.js";
|
||||||
import { platform } from "../platform/api.js";
|
import { platform } from "../platform/api.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
export function useClipboardPermissions(): ToggleHandler {
|
export function useClipboardPermissions(): ToggleHandler {
|
||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
const [error, setError] = useState<TalerError | undefined>();
|
const [error, setError] = useState<TalerError | undefined>();
|
||||||
|
const api = useBackendContext()
|
||||||
|
|
||||||
const toggle = async (): Promise<void> => {
|
const toggle = async (): Promise<void> => {
|
||||||
return handleClipboardPerm(enabled, setEnabled).catch((e) => {
|
return handleClipboardPerm(enabled, setEnabled, api.background).catch((e) => {
|
||||||
setError(TalerError.fromException(e));
|
setError(TalerError.fromException(e));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getValue(): Promise<void> {
|
async function getValue(): Promise<void> {
|
||||||
const res = await wxApi.background.containsHeaderListener();
|
const res = await api.background.containsHeaderListener();
|
||||||
setEnabled(res.newValue);
|
setEnabled(res.newValue);
|
||||||
}
|
}
|
||||||
getValue();
|
getValue();
|
||||||
@ -49,6 +51,7 @@ export function useClipboardPermissions(): ToggleHandler {
|
|||||||
async function handleClipboardPerm(
|
async function handleClipboardPerm(
|
||||||
isEnabled: boolean,
|
isEnabled: boolean,
|
||||||
onChange: (value: boolean) => void,
|
onChange: (value: boolean) => void,
|
||||||
|
background: ReturnType<typeof useBackendContext>["background"],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
// We set permissions here, since apparently FF wants this to be done
|
// We set permissions here, since apparently FF wants this to be done
|
||||||
@ -62,11 +65,10 @@ async function handleClipboardPerm(
|
|||||||
onChange(false);
|
onChange(false);
|
||||||
throw lastError;
|
throw lastError;
|
||||||
}
|
}
|
||||||
// const res = await wxApi.toggleHeaderListener(granted);
|
|
||||||
onChange(granted);
|
onChange(granted);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await wxApi.background
|
await background
|
||||||
.toggleHeaderListener(false)
|
.toggleHeaderListener(false)
|
||||||
.then((r) => onChange(r.newValue));
|
.then((r) => onChange(r.newValue));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
import { WalletDiagnostics } from "@gnu-taler/taler-util";
|
import { WalletDiagnostics } from "@gnu-taler/taler-util";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { wxApi } from "../wxApi.js";
|
import { useBackendContext } from "../context/backend.js";
|
||||||
|
|
||||||
export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
|
export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
|
||||||
const [timedOut, setTimedOut] = useState(false);
|
const [timedOut, setTimedOut] = useState(false);
|
||||||
|
const api = useBackendContext();
|
||||||
const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
|
const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
@ -33,7 +34,7 @@ export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
|
|||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
const doFetch = async (): Promise<void> => {
|
const doFetch = async (): Promise<void> => {
|
||||||
const d = await wxApi.background.getDiagnostics();
|
const d = await api.background.getDiagnostics();
|
||||||
gotDiagnostics = true;
|
gotDiagnostics = true;
|
||||||
setDiagnostics(d);
|
setDiagnostics(d);
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { ProviderInfo, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { ProviderInfo, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { wxApi } from "../wxApi.js";
|
import { useBackendContext } from "../context/backend.js";
|
||||||
|
|
||||||
export interface ProviderStatus {
|
export interface ProviderStatus {
|
||||||
info?: ProviderInfo;
|
info?: ProviderInfo;
|
||||||
@ -26,11 +26,11 @@ export interface ProviderStatus {
|
|||||||
|
|
||||||
export function useProviderStatus(url: string): ProviderStatus | undefined {
|
export function useProviderStatus(url: string): ProviderStatus | undefined {
|
||||||
const [status, setStatus] = useState<ProviderStatus | undefined>(undefined);
|
const [status, setStatus] = useState<ProviderStatus | undefined>(undefined);
|
||||||
|
const api = useBackendContext();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
//create a first list of backup info by currency
|
//create a first list of backup info by currency
|
||||||
const status = await wxApi.wallet.call(
|
const status = await api.wallet.call(
|
||||||
WalletApiOperation.GetBackupInfo,
|
WalletApiOperation.GetBackupInfo,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
@ -42,7 +42,7 @@ export function useProviderStatus(url: string): ProviderStatus | undefined {
|
|||||||
|
|
||||||
async function sync(): Promise<void> {
|
async function sync(): Promise<void> {
|
||||||
if (info) {
|
if (info) {
|
||||||
await wxApi.wallet.call(WalletApiOperation.RunBackupCycle, {
|
await api.wallet.call(WalletApiOperation.RunBackupCycle, {
|
||||||
providers: [info.syncProviderBaseUrl],
|
providers: [info.syncProviderBaseUrl],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ export function useProviderStatus(url: string): ProviderStatus | undefined {
|
|||||||
|
|
||||||
async function remove(): Promise<void> {
|
async function remove(): Promise<void> {
|
||||||
if (info) {
|
if (info) {
|
||||||
await wxApi.wallet.call(WalletApiOperation.RemoveBackupProvider, {
|
await api.wallet.call(WalletApiOperation.RemoveBackupProvider, {
|
||||||
provider: info.syncProviderBaseUrl,
|
provider: info.syncProviderBaseUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect } from "preact/hooks";
|
import { useState, useEffect } from "preact/hooks";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
import { ToggleHandler } from "../mui/handlers.js";
|
import { ToggleHandler } from "../mui/handlers.js";
|
||||||
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
|
|
||||||
export function useWalletDevMode(): ToggleHandler {
|
export function useWalletDevMode(): ToggleHandler {
|
||||||
const [enabled, setEnabled] = useState<undefined | boolean>(undefined);
|
const [enabled, setEnabled] = useState<undefined | boolean>(undefined);
|
||||||
const [error, setError] = useState<TalerError | undefined>();
|
const [error, setError] = useState<TalerError | undefined>();
|
||||||
|
const api = useBackendContext();
|
||||||
|
|
||||||
const toggle = async (): Promise<void> => {
|
const toggle = async (): Promise<void> => {
|
||||||
return handleOpen(enabled, setEnabled).catch((e) => {
|
return handleOpen(enabled, setEnabled, api).catch((e) => {
|
||||||
setError(TalerError.fromException(e));
|
setError(TalerError.fromException(e));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getValue(): Promise<void> {
|
async function getValue(): Promise<void> {
|
||||||
const res = await wxApi.wallet.call(WalletApiOperation.GetVersion, {});
|
const res = await api.wallet.call(WalletApiOperation.GetVersion, {});
|
||||||
setEnabled(res.devMode);
|
setEnabled(res.devMode);
|
||||||
}
|
}
|
||||||
getValue();
|
getValue();
|
||||||
@ -47,9 +49,10 @@ export function useWalletDevMode(): ToggleHandler {
|
|||||||
async function handleOpen(
|
async function handleOpen(
|
||||||
currentValue: undefined | boolean,
|
currentValue: undefined | boolean,
|
||||||
onChange: (value: boolean) => void,
|
onChange: (value: boolean) => void,
|
||||||
|
api: ReturnType<typeof useBackendContext>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const nextValue = !currentValue;
|
const nextValue = !currentValue;
|
||||||
await wxApi.wallet.call(WalletApiOperation.SetDevMode, {
|
await api.wallet.call(WalletApiOperation.SetDevMode, {
|
||||||
devModeEnabled: nextValue,
|
devModeEnabled: nextValue,
|
||||||
});
|
});
|
||||||
onChange(nextValue);
|
onChange(nextValue);
|
||||||
|
@ -22,13 +22,13 @@ import { BalanceTable } from "../components/BalanceTable.js";
|
|||||||
import { Loading } from "../components/Loading.js";
|
import { Loading } from "../components/Loading.js";
|
||||||
import { LoadingError } from "../components/LoadingError.js";
|
import { LoadingError } from "../components/LoadingError.js";
|
||||||
import { MultiActionButton } from "../components/MultiActionButton.js";
|
import { MultiActionButton } from "../components/MultiActionButton.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { ButtonHandler } from "../mui/handlers.js";
|
import { ButtonHandler } from "../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../utils/index.js";
|
import { compose, StateViewMap } from "../utils/index.js";
|
||||||
import { AddNewActionView } from "../wallet/AddNewActionView.js";
|
import { AddNewActionView } from "../wallet/AddNewActionView.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
import { NoBalanceHelp } from "./NoBalanceHelp.js";
|
import { NoBalanceHelp } from "./NoBalanceHelp.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -67,10 +67,12 @@ export namespace State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useComponentState(
|
function useComponentState({
|
||||||
{ goToWalletDeposit, goToWalletHistory, goToWalletManualWithdraw }: Props,
|
goToWalletDeposit,
|
||||||
api: typeof wxApi,
|
goToWalletHistory,
|
||||||
): State {
|
goToWalletManualWithdraw,
|
||||||
|
}: Props): State {
|
||||||
|
const api = useBackendContext();
|
||||||
const [addingAction, setAddingAction] = useState(false);
|
const [addingAction, setAddingAction] = useState(false);
|
||||||
const state = useAsyncAsHook(() =>
|
const state = useAsyncAsHook(() =>
|
||||||
api.wallet.call(WalletApiOperation.GetBalances, {}),
|
api.wallet.call(WalletApiOperation.GetBalances, {}),
|
||||||
@ -128,7 +130,7 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const BalancePage = compose(
|
export const BalancePage = compose(
|
||||||
"BalancePage",
|
"BalancePage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
VNode,
|
VNode,
|
||||||
} from "preact";
|
} from "preact";
|
||||||
import { render as renderToString } from "preact-render-to-string";
|
import { render as renderToString } from "preact-render-to-string";
|
||||||
|
import { BackendProvider } from "./context/backend.js";
|
||||||
import { BackgroundApiClient, wxApi } from "./wxApi.js";
|
import { BackgroundApiClient, wxApi } from "./wxApi.js";
|
||||||
|
|
||||||
// When doing tests we want the requestAnimationFrame to be as fast as possible.
|
// When doing tests we want the requestAnimationFrame to be as fast as possible.
|
||||||
@ -252,7 +253,7 @@ type Subscriptions = {
|
|||||||
|
|
||||||
export function createWalletApiMock(): {
|
export function createWalletApiMock(): {
|
||||||
handler: MockHandler;
|
handler: MockHandler;
|
||||||
mock: typeof wxApi;
|
TestingContext: FunctionalComponent<{ children: ComponentChildren }>
|
||||||
} {
|
} {
|
||||||
const calls = new Array<CallRecord>();
|
const calls = new Array<CallRecord>();
|
||||||
const subscriptions: Subscriptions = {};
|
const subscriptions: Subscriptions = {};
|
||||||
@ -357,5 +358,14 @@ export function createWalletApiMock(): {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handler, mock };
|
function TestingContext({ children }: { children: ComponentChildren }): VNode {
|
||||||
|
return create(BackendProvider, {
|
||||||
|
wallet: mock.wallet,
|
||||||
|
background: mock.background,
|
||||||
|
listener: mock.listener,
|
||||||
|
children,
|
||||||
|
}, children)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { handler, TestingContext };
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
TalerErrorDetail
|
||||||
BackupBackupProviderTerms,
|
|
||||||
TalerErrorDetail,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
|
import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
@ -25,15 +23,13 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
|||||||
import {
|
import {
|
||||||
ButtonHandler,
|
ButtonHandler,
|
||||||
TextFieldHandler,
|
TextFieldHandler,
|
||||||
ToggleHandler,
|
ToggleHandler
|
||||||
} from "../../mui/handlers.js";
|
} from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import {
|
import {
|
||||||
LoadingUriView,
|
ConfirmProviderView, LoadingUriView,
|
||||||
SelectProviderView,
|
SelectProviderView
|
||||||
ConfirmProviderView,
|
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -90,6 +86,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const AddBackupProviderPage = compose(
|
export const AddBackupProviderPage = compose(
|
||||||
"AddBackupProvider",
|
"AddBackupProvider",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,15 +17,15 @@
|
|||||||
import {
|
import {
|
||||||
canonicalizeBaseUrl,
|
canonicalizeBaseUrl,
|
||||||
Codec,
|
Codec,
|
||||||
TalerErrorDetail,
|
TalerErrorDetail
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
codecForSyncTermsOfServiceResponse,
|
codecForSyncTermsOfServiceResponse,
|
||||||
WalletApiOperation,
|
WalletApiOperation
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { assertUnreachable } from "../../utils/index.js";
|
import { assertUnreachable } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
type UrlState<T> = UrlOk<T> | UrlError;
|
type UrlState<T> = UrlOk<T> | UrlError;
|
||||||
@ -106,37 +106,37 @@ function useUrlState<T>(
|
|||||||
constHref == undefined
|
constHref == undefined
|
||||||
? undefined
|
? undefined
|
||||||
: async () => {
|
: async () => {
|
||||||
const req = await fetch(constHref).catch((e) => {
|
const req = await fetch(constHref).catch((e) => {
|
||||||
return setState({
|
return setState({
|
||||||
status: "network-error",
|
status: "network-error",
|
||||||
href: constHref,
|
href: constHref,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
if (!req) return;
|
});
|
||||||
|
if (!req) return;
|
||||||
|
|
||||||
if (req.status >= 400 && req.status < 500) {
|
if (req.status >= 400 && req.status < 500) {
|
||||||
setState({
|
setState({
|
||||||
status: "client-error",
|
status: "client-error",
|
||||||
code: req.status,
|
code: req.status,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (req.status > 500) {
|
if (req.status > 500) {
|
||||||
setState({
|
setState({
|
||||||
status: "server-error",
|
status: "server-error",
|
||||||
code: req.status,
|
code: req.status,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await req.json();
|
const json = await req.json();
|
||||||
try {
|
try {
|
||||||
const result = codec.decode(json);
|
const result = codec.decode(json);
|
||||||
setState({ status: "ok", result });
|
setState({ status: "ok", result });
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setState({ status: "parsing-error", json });
|
setState({ status: "parsing-error", json });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[host, path],
|
[host, path],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -145,8 +145,8 @@ function useUrlState<T>(
|
|||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ currency, onBack, onComplete, onPaymentRequired }: Props,
|
{ currency, onBack, onComplete, onPaymentRequired }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const [url, setHost] = useState<string | undefined>();
|
const [url, setHost] = useState<string | undefined>();
|
||||||
const [name, setName] = useState<string | undefined>();
|
const [name, setName] = useState<string | undefined>();
|
||||||
const [tos, setTos] = useState(false);
|
const [tos, setTos] = useState(false);
|
||||||
@ -223,8 +223,8 @@ export function useComponentState(
|
|||||||
!urlState || urlState.status !== "ok" || !name
|
!urlState || urlState.status !== "ok" || !name
|
||||||
? undefined
|
? undefined
|
||||||
: async () => {
|
: async () => {
|
||||||
setShowConfirm(true);
|
setShowConfirm(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
urlOk: urlState?.status === "ok",
|
urlOk: urlState?.status === "ok",
|
||||||
url: {
|
url: {
|
||||||
|
@ -19,12 +19,10 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
import {
|
import {
|
||||||
createWalletApiMock,
|
createWalletApiMock, nullFunction
|
||||||
mountHook,
|
|
||||||
nullFunction,
|
|
||||||
} from "../../test-utils.js";
|
} from "../../test-utils.js";
|
||||||
import { Props } from "./index.js";
|
import { Props } from "./index.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
@ -36,44 +34,21 @@ const props: Props = {
|
|||||||
onPaymentRequired: nullFunction,
|
onPaymentRequired: nullFunction,
|
||||||
};
|
};
|
||||||
describe("AddBackupProvider states", () => {
|
describe("AddBackupProvider states", () => {
|
||||||
|
|
||||||
it("should start in 'select-provider' state", async () => {
|
it("should start in 'select-provider' state", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
// handler.addWalletCallResponse(
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
// WalletApiOperation.ListKnownBankAccounts,
|
(state) => {
|
||||||
// undefined,
|
expect(state.status).equal("select-provider");
|
||||||
// {
|
if (state.status !== "select-provider") return;
|
||||||
// accounts: [],
|
expect(state.name.value).eq("");
|
||||||
// },
|
expect(state.url.value).eq("");
|
||||||
// );
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
expect(state.status).equal("select-provider");
|
|
||||||
if (state.status !== "select-provider") return;
|
|
||||||
expect(state.name.value).eq("");
|
|
||||||
expect(state.url.value).eq("");
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIXME: this should not make an extra update
|
|
||||||
/**
|
|
||||||
* this may be due to useUrlState because is using an effect over
|
|
||||||
* a dependency with a timeout
|
|
||||||
*/
|
|
||||||
// NOTE: do not remove this comment, keeping as an example
|
|
||||||
// await waitForStateUpdate()
|
|
||||||
// {
|
|
||||||
// const state = pullLastResultOrThrow();
|
|
||||||
// expect(state.status).equal("select-provider");
|
|
||||||
// if (state.status !== "select-provider") return;
|
|
||||||
// expect(state.name.value).eq("")
|
|
||||||
// expect(state.url.value).eq("")
|
|
||||||
// }
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,11 +42,11 @@ import {
|
|||||||
SmallText,
|
SmallText,
|
||||||
WarningBox,
|
WarningBox,
|
||||||
} from "../components/styled/index.js";
|
} from "../components/styled/index.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { Pages } from "../NavigationBar.js";
|
import { Pages } from "../NavigationBar.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onAddProvider: () => Promise<void>;
|
onAddProvider: () => Promise<void>;
|
||||||
@ -107,8 +107,9 @@ export function ShowRecoveryInfo({
|
|||||||
|
|
||||||
export function BackupPage({ onAddProvider }: Props): VNode {
|
export function BackupPage({ onAddProvider }: Props): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
const api = useBackendContext();
|
||||||
const status = useAsyncAsHook(() =>
|
const status = useAsyncAsHook(() =>
|
||||||
wxApi.wallet.call(WalletApiOperation.GetBackupInfo, {}),
|
api.wallet.call(WalletApiOperation.GetBackupInfo, {}),
|
||||||
);
|
);
|
||||||
const [recoveryInfo, setRecoveryInfo] = useState<string>("");
|
const [recoveryInfo, setRecoveryInfo] = useState<string>("");
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -124,7 +125,7 @@ export function BackupPage({ onAddProvider }: Props): VNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getRecoveryInfo(): Promise<void> {
|
async function getRecoveryInfo(): Promise<void> {
|
||||||
const r = await wxApi.wallet.call(
|
const r = await api.wallet.call(
|
||||||
WalletApiOperation.ExportBackupRecovery,
|
WalletApiOperation.ExportBackupRecovery,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
@ -158,7 +159,7 @@ export function BackupPage({ onAddProvider }: Props): VNode {
|
|||||||
providers={providers}
|
providers={providers}
|
||||||
onAddProvider={onAddProvider}
|
onAddProvider={onAddProvider}
|
||||||
onSyncAll={async () =>
|
onSyncAll={async () =>
|
||||||
wxApi.wallet.call(WalletApiOperation.RunBackupCycle, {}).then()
|
api.wallet.call(WalletApiOperation.RunBackupCycle, {}).then()
|
||||||
}
|
}
|
||||||
onShowInfo={getRecoveryInfo}
|
onShowInfo={getRecoveryInfo}
|
||||||
/>
|
/>
|
||||||
|
@ -20,11 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
|||||||
import {
|
import {
|
||||||
AmountFieldHandler,
|
AmountFieldHandler,
|
||||||
ButtonHandler,
|
ButtonHandler,
|
||||||
SelectFieldHandler,
|
SelectFieldHandler
|
||||||
TextFieldHandler,
|
|
||||||
} from "../../mui/handlers.js";
|
} from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { ManageAccountPage } from "../ManageAccount/index.js";
|
import { ManageAccountPage } from "../ManageAccount/index.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import {
|
import {
|
||||||
@ -32,7 +30,7 @@ import {
|
|||||||
LoadingErrorView,
|
LoadingErrorView,
|
||||||
NoAccountToDepositView,
|
NoAccountToDepositView,
|
||||||
NoEnoughBalanceView,
|
NoEnoughBalanceView,
|
||||||
ReadyView,
|
ReadyView
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -119,6 +117,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const DepositPage = compose(
|
export const DepositPage = compose(
|
||||||
"DepositPage",
|
"DepositPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -21,18 +21,18 @@ import {
|
|||||||
KnownBankAccountsInfo,
|
KnownBankAccountsInfo,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
PaytoUri,
|
PaytoUri,
|
||||||
stringifyPaytoUri,
|
stringifyPaytoUri
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props,
|
{ amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
|
const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
|
||||||
const currency = parsed !== undefined ? parsed.currency : currencyStr;
|
const currency = parsed !== undefined ? parsed.currency : currencyStr;
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ export function useComponentState(
|
|||||||
parsed !== undefined
|
parsed !== undefined
|
||||||
? parsed
|
? parsed
|
||||||
: currency !== undefined
|
: currency !== undefined
|
||||||
? Amounts.zeroOfCurrency(currency)
|
? Amounts.zeroOfCurrency(currency)
|
||||||
: undefined;
|
: undefined;
|
||||||
// const [accountIdx, setAccountIdx] = useState<number>(0);
|
// const [accountIdx, setAccountIdx] = useState<number>(0);
|
||||||
const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as any));
|
const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as any));
|
||||||
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
|
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
|
||||||
@ -134,7 +134,7 @@ export function useComponentState(
|
|||||||
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
|
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
|
||||||
|
|
||||||
if (fee === undefined) {
|
if (fee === undefined) {
|
||||||
getFeeForAmount(currentAccount, amount, api).then((initialFee) => {
|
getFeeForAmount(currentAccount, amount, api.wallet).then((initialFee) => {
|
||||||
setFee(initialFee);
|
setFee(initialFee);
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@ -149,7 +149,7 @@ export function useComponentState(
|
|||||||
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
|
const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
|
||||||
if (uri) {
|
if (uri) {
|
||||||
try {
|
try {
|
||||||
const result = await getFeeForAmount(uri, amount, api);
|
const result = await getFeeForAmount(uri, amount, api.wallet);
|
||||||
setSelectedAccount(uri);
|
setSelectedAccount(uri);
|
||||||
setFee(result);
|
setFee(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -162,7 +162,7 @@ export function useComponentState(
|
|||||||
async function updateAmount(newAmount: AmountJson): Promise<void> {
|
async function updateAmount(newAmount: AmountJson): Promise<void> {
|
||||||
// const parsed = Amounts.parse(`${currency}:${numStr}`);
|
// const parsed = Amounts.parse(`${currency}:${numStr}`);
|
||||||
try {
|
try {
|
||||||
const result = await getFeeForAmount(currentAccount, newAmount, api);
|
const result = await getFeeForAmount(currentAccount, newAmount, api.wallet);
|
||||||
setAmount(newAmount);
|
setAmount(newAmount);
|
||||||
setFee(result);
|
setFee(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -185,8 +185,8 @@ export function useComponentState(
|
|||||||
const amountError = !isDirty
|
const amountError = !isDirty
|
||||||
? undefined
|
? undefined
|
||||||
: Amounts.cmp(balance, amount) === -1
|
: Amounts.cmp(balance, amount) === -1
|
||||||
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
|
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const unableToDeposit =
|
const unableToDeposit =
|
||||||
Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
|
Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
|
||||||
@ -243,11 +243,11 @@ export function useComponentState(
|
|||||||
async function getFeeForAmount(
|
async function getFeeForAmount(
|
||||||
p: PaytoUri,
|
p: PaytoUri,
|
||||||
a: AmountJson,
|
a: AmountJson,
|
||||||
api: typeof wxApi,
|
wallet: ReturnType<typeof useBackendContext>["wallet"],
|
||||||
): Promise<DepositGroupFees> {
|
): Promise<DepositGroupFees> {
|
||||||
const depositPaytoUri = `payto://${p.targetType}/${p.targetPath}`;
|
const depositPaytoUri = `payto://${p.targetType}/${p.targetPath}`;
|
||||||
const amount = Amounts.stringify(a);
|
const amount = Amounts.stringify(a);
|
||||||
return await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
|
return await wallet.call(WalletApiOperation.GetFeeForDeposit, {
|
||||||
amount,
|
amount,
|
||||||
depositPaytoUri,
|
depositPaytoUri,
|
||||||
});
|
});
|
||||||
|
@ -23,14 +23,13 @@ import {
|
|||||||
Amounts,
|
Amounts,
|
||||||
DepositGroupFees,
|
DepositGroupFees,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
stringifyPaytoUri,
|
stringifyPaytoUri
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
import {
|
import {
|
||||||
createWalletApiMock,
|
createWalletApiMock, nullFunction
|
||||||
mountHook,
|
|
||||||
nullFunction,
|
|
||||||
} from "../../test-utils.js";
|
} from "../../test-utils.js";
|
||||||
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
@ -50,7 +49,7 @@ const withSomeFee = (): DepositGroupFees => ({
|
|||||||
|
|
||||||
describe("DepositPage states", () => {
|
describe("DepositPage states", () => {
|
||||||
it("should have status 'no-enough-balance' when balance is empty", async () => {
|
it("should have status 'no-enough-balance' when balance is empty", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
||||||
@ -72,27 +71,21 @@ describe("DepositPage states", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equal("loading");
|
||||||
|
},
|
||||||
|
({ status }) => {
|
||||||
|
expect(status).equal("no-enough-balance");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("no-enough-balance");
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have status 'no-accounts' when balance is not empty and accounts is empty", async () => {
|
it("should have status 'no-accounts' when balance is not empty and accounts is empty", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
||||||
@ -113,22 +106,17 @@ describe("DepositPage states", () => {
|
|||||||
accounts: [],
|
accounts: [],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const { status } = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
expect(status).equal("loading");
|
expect(status).equal("loading");
|
||||||
}
|
},
|
||||||
|
({ status }) => {
|
||||||
|
expect(status).equal("no-accounts");
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "no-accounts") expect.fail();
|
|
||||||
// expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -146,7 +134,7 @@ describe("DepositPage states", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it("should have status 'ready' but unable to deposit ", async () => {
|
it("should have status 'ready' but unable to deposit ", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
||||||
@ -173,37 +161,29 @@ describe("DepositPage states", () => {
|
|||||||
withoutFee(),
|
withoutFee(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
mountHook(() => useComponentState(props, mock));
|
({ status }) => {
|
||||||
|
expect(status).equal("loading");
|
||||||
|
},
|
||||||
|
({ status }) => {
|
||||||
|
expect(status).equal("loading");
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") expect.fail();
|
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
expect(r.currency).eq(currency);
|
|
||||||
expect(r.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
|
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
|
||||||
expect(r.depositHandler.onClick).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be able to deposit more than the balance ", async () => {
|
it("should not be able to deposit more than the balance ", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
||||||
@ -230,139 +210,50 @@ describe("DepositPage states", () => {
|
|||||||
withoutFee(),
|
withoutFee(),
|
||||||
);
|
);
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
|
||||||
WalletApiOperation.GetFeeForDeposit,
|
|
||||||
undefined,
|
|
||||||
withoutFee(),
|
|
||||||
);
|
|
||||||
handler.addWalletCallResponse(
|
|
||||||
WalletApiOperation.GetFeeForDeposit,
|
|
||||||
undefined,
|
|
||||||
withoutFee(),
|
|
||||||
);
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.GetFeeForDeposit,
|
WalletApiOperation.GetFeeForDeposit,
|
||||||
undefined,
|
undefined,
|
||||||
withoutFee(),
|
withoutFee(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
|
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const r = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
if (r.status !== "ready") expect.fail();
|
expect(status).equal("loading");
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
},
|
||||||
expect(r.currency).eq(currency);
|
({ status }) => {
|
||||||
expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
|
expect(status).equal("loading");
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
},
|
||||||
expect(r.depositHandler.onClick).undefined;
|
(state) => {
|
||||||
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
if (state.status !== "ready") expect.fail();
|
||||||
expect(r.account.onChange).not.undefined;
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
|
expect(state.account.onChange).not.undefined;
|
||||||
|
|
||||||
r.account.onChange!(accountSelected);
|
state.account.onChange!(accountSelected);
|
||||||
}
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(accountSelected);
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") expect.fail();
|
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
expect(r.currency).eq(currency);
|
|
||||||
expect(r.account.value).eq(accountSelected);
|
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
|
||||||
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
|
||||||
expect(r.depositHandler.onClick).undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// it("should calculate the fee upon entering amount ", async () => {
|
|
||||||
// const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
// mountHook(() =>
|
|
||||||
// useComponentState(
|
|
||||||
// { currency, onCancel: nullFunction, onSuccess: nullFunction },
|
|
||||||
// {
|
|
||||||
// getBalance: async () =>
|
|
||||||
// ({
|
|
||||||
// balances: [{ available: `${currency}:1` }],
|
|
||||||
// } as Partial<BalancesResponse>),
|
|
||||||
// listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }),
|
|
||||||
// getFeeForDeposit: withSomeFee,
|
|
||||||
// } as Partial<typeof wxApi> as any,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// {
|
|
||||||
// const { status } = getLastResultOrThrow();
|
|
||||||
// expect(status).equal("loading");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await waitNextUpdate();
|
|
||||||
|
|
||||||
// {
|
|
||||||
// const r = getLastResultOrThrow();
|
|
||||||
// if (r.status !== "ready") expect.fail();
|
|
||||||
// expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
// expect(r.currency).eq(currency);
|
|
||||||
// expect(r.account.value).eq(accountSelected);
|
|
||||||
// expect(r.amount.value).eq("0");
|
|
||||||
// expect(r.depositHandler.onClick).undefined;
|
|
||||||
// expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
|
||||||
|
|
||||||
// r.amount.onInput("10");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
// {
|
|
||||||
// const r = pullLastResultOrThrow();
|
|
||||||
// if (r.status !== "ready") expect.fail();
|
|
||||||
// expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
// expect(r.currency).eq(currency);
|
|
||||||
// expect(r.account.value).eq(accountSelected);
|
|
||||||
// expect(r.amount.value).eq("10");
|
|
||||||
// expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
|
||||||
// expect(r.depositHandler.onClick).undefined;
|
|
||||||
|
|
||||||
// r.amount.onInput("3");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
// {
|
|
||||||
// const r = pullLastResultOrThrow();
|
|
||||||
// if (r.status !== "ready") expect.fail();
|
|
||||||
// expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
// expect(r.currency).eq(currency);
|
|
||||||
// expect(r.account.value).eq(accountSelected);
|
|
||||||
// expect(r.amount.value).eq("3");
|
|
||||||
// expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
|
||||||
// expect(r.depositHandler.onClick).not.undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await assertNoPendingUpdate();
|
|
||||||
// expect(handler.getCallingQueueState()).eq("empty")
|
|
||||||
// });
|
|
||||||
|
|
||||||
it("should calculate the fee upon entering amount ", async () => {
|
it("should calculate the fee upon entering amount ", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
const props = { currency, onCancel: nullFunction, onSuccess: nullFunction };
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
|
||||||
@ -399,70 +290,54 @@ describe("DepositPage states", () => {
|
|||||||
withSomeFee(),
|
withSomeFee(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const { status } = pullLastResultOrThrow();
|
|
||||||
expect(status).equal("loading");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
|
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const r = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
if (r.status !== "ready") expect.fail();
|
expect(status).equal("loading");
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
},
|
||||||
expect(r.currency).eq(currency);
|
({ status }) => {
|
||||||
expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
|
expect(status).equal("loading");
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
},
|
||||||
expect(r.depositHandler.onClick).undefined;
|
(state) => {
|
||||||
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
if (state.status !== "ready") expect.fail();
|
||||||
expect(r.account.onChange).not.undefined;
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
|
||||||
|
expect(state.account.onChange).not.undefined;
|
||||||
|
|
||||||
r.account.onChange!(accountSelected);
|
state.account.onChange!(accountSelected);
|
||||||
}
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(accountSelected);
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
||||||
|
expect(state.depositHandler.onClick).undefined;
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state.amount.onInput).not.undefined;
|
||||||
|
if (!state.amount.onInput) return;
|
||||||
|
state.amount.onInput(Amounts.parseOrThrow("EUR:10"));
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
expect(state.cancelHandler.onClick).not.undefined;
|
||||||
|
expect(state.currency).eq(currency);
|
||||||
|
expect(state.account.value).eq(accountSelected);
|
||||||
|
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
|
||||||
|
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
||||||
|
expect(state.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`));
|
||||||
|
expect(state.depositHandler.onClick).not.undefined;
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") expect.fail();
|
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
expect(r.currency).eq(currency);
|
|
||||||
expect(r.account.value).eq(accountSelected);
|
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
|
|
||||||
expect(r.depositHandler.onClick).undefined;
|
|
||||||
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
|
||||||
|
|
||||||
expect(r.amount.onInput).not.undefined;
|
|
||||||
if (!r.amount.onInput) return;
|
|
||||||
r.amount.onInput(Amounts.parseOrThrow("EUR:10"));
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const r = pullLastResultOrThrow();
|
|
||||||
if (r.status !== "ready") expect.fail();
|
|
||||||
expect(r.cancelHandler.onClick).not.undefined;
|
|
||||||
expect(r.currency).eq(currency);
|
|
||||||
expect(r.account.value).eq(accountSelected);
|
|
||||||
expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
|
|
||||||
expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
|
|
||||||
expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`));
|
|
||||||
expect(r.depositHandler.onClick).not.undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,6 @@ import { Loading } from "../../components/Loading.js";
|
|||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { AmountFieldHandler, ButtonHandler } from "../../mui/handlers.js";
|
import { AmountFieldHandler, ButtonHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView, SelectCurrencyView } from "./views.js";
|
import { LoadingUriView, ReadyView, SelectCurrencyView } from "./views.js";
|
||||||
|
|
||||||
@ -88,6 +87,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const DestinationSelectionPage = compose(
|
export const DestinationSelectionPage = compose(
|
||||||
"DestinationSelectionPage",
|
"DestinationSelectionPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,15 +17,15 @@
|
|||||||
import { Amounts } from "@gnu-taler/taler-util";
|
import { Amounts } from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { assertUnreachable, RecursiveState } from "../../utils/index.js";
|
import { assertUnreachable, RecursiveState } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Contact, Props, State } from "./index.js";
|
import { Contact, Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
props: Props,
|
props: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): RecursiveState<State> {
|
): RecursiveState<State> {
|
||||||
|
const api = useBackendContext()
|
||||||
const parsedInitialAmount = !props.amount
|
const parsedInitialAmount = !props.amount
|
||||||
? undefined
|
? undefined
|
||||||
: Amounts.parse(props.amount);
|
: Amounts.parse(props.amount);
|
||||||
|
@ -23,11 +23,12 @@ import {
|
|||||||
Amounts,
|
Amounts,
|
||||||
ExchangeEntryStatus,
|
ExchangeEntryStatus,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangeTosStatus,
|
ExchangeTosStatus
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { createWalletApiMock, mountHook } from "../../test-utils.js";
|
import { tests } from "../../../../web-util/src/index.browser.js";
|
||||||
|
import { createWalletApiMock, nullFunction } from "../../test-utils.js";
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
|
|
||||||
const exchangeArs: ExchangeListItem = {
|
const exchangeArs: ExchangeListItem = {
|
||||||
@ -42,7 +43,7 @@ const exchangeArs: ExchangeListItem = {
|
|||||||
|
|
||||||
describe("Destination selection states", () => {
|
describe("Destination selection states", () => {
|
||||||
it("should select currency if no amount specified", async () => {
|
it("should select currency if no amount specified", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.ListExchanges,
|
WalletApiOperation.ListExchanges,
|
||||||
@ -54,83 +55,65 @@ describe("Destination selection states", () => {
|
|||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
type: "get" as const,
|
type: "get" as const,
|
||||||
goToWalletManualWithdraw: () => {
|
goToWalletManualWithdraw: nullFunction,
|
||||||
return null;
|
goToWalletWalletInvoice: nullFunction,
|
||||||
},
|
|
||||||
goToWalletWalletInvoice: () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const state = pullLastResultOrThrow();
|
({ status }) => {
|
||||||
|
expect(status).equal("loading");
|
||||||
|
},
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "select-currency") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.currencies).deep.eq({
|
||||||
|
ARS: "ARS",
|
||||||
|
"": "Select a currency",
|
||||||
|
});
|
||||||
|
|
||||||
if (state.status !== "loading") expect.fail();
|
state.onCurrencySelected(exchangeArs.currency!);
|
||||||
if (state.error) expect.fail();
|
},
|
||||||
}
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.goToBank.onClick).eq(undefined);
|
||||||
|
expect(state.goToWallet.onClick).eq(undefined);
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0"));
|
||||||
|
},
|
||||||
|
], TestingContext)
|
||||||
|
|
||||||
{
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "select-currency") expect.fail();
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.currencies).deep.eq({
|
|
||||||
ARS: "ARS",
|
|
||||||
"": "Select a currency",
|
|
||||||
});
|
|
||||||
|
|
||||||
state.onCurrencySelected(exchangeArs.currency!);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(await waitForStateUpdate()).true;
|
|
||||||
|
|
||||||
{
|
|
||||||
const state = pullLastResultOrThrow();
|
|
||||||
|
|
||||||
if (state.status !== "ready") expect.fail();
|
|
||||||
if (state.error) expect.fail();
|
|
||||||
expect(state.goToBank.onClick).eq(undefined);
|
|
||||||
expect(state.goToWallet.onClick).eq(undefined);
|
|
||||||
|
|
||||||
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be possible to start with an amount specified in request params", async () => {
|
it("should be possible to start with an amount specified in request params", async () => {
|
||||||
const { handler, mock } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
type: "get" as const,
|
type: "get" as const,
|
||||||
goToWalletManualWithdraw: () => {
|
goToWalletManualWithdraw: nullFunction,
|
||||||
return null;
|
goToWalletWalletInvoice: nullFunction,
|
||||||
},
|
|
||||||
goToWalletWalletInvoice: () => {
|
|
||||||
null;
|
|
||||||
},
|
|
||||||
amount: "ARS:2",
|
amount: "ARS:2",
|
||||||
};
|
};
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
|
||||||
mountHook(() => useComponentState(props, mock));
|
|
||||||
|
|
||||||
{
|
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
|
||||||
const state = pullLastResultOrThrow();
|
// ({ status }) => {
|
||||||
|
// expect(status).equal("loading");
|
||||||
|
// },
|
||||||
|
(state) => {
|
||||||
|
if (state.status !== "ready") expect.fail();
|
||||||
|
if (state.error) expect.fail();
|
||||||
|
expect(state.goToBank.onClick).not.eq(undefined);
|
||||||
|
expect(state.goToWallet.onClick).not.eq(undefined);
|
||||||
|
|
||||||
if (state.status !== "ready") expect.fail();
|
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:2"));
|
||||||
if (state.error) expect.fail();
|
},
|
||||||
expect(state.goToBank.onClick).not.eq(undefined);
|
], TestingContext)
|
||||||
expect(state.goToWallet.onClick).not.eq(undefined);
|
|
||||||
|
|
||||||
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:2"));
|
expect(hookBehavior).deep.equal({ result: "ok" })
|
||||||
}
|
|
||||||
|
|
||||||
await assertNoPendingUpdate();
|
|
||||||
expect(handler.getCallingQueueState()).eq("empty");
|
expect(handler.getCallingQueueState()).eq("empty");
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,12 +31,12 @@ import { useEffect, useRef, useState } from "preact/hooks";
|
|||||||
import { Diagnostics } from "../components/Diagnostics.js";
|
import { Diagnostics } from "../components/Diagnostics.js";
|
||||||
import { NotifyUpdateFadeOut } from "../components/styled/index.js";
|
import { NotifyUpdateFadeOut } from "../components/styled/index.js";
|
||||||
import { Time } from "../components/Time.js";
|
import { Time } from "../components/Time.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { useDiagnostics } from "../hooks/useDiagnostics.js";
|
import { useDiagnostics } from "../hooks/useDiagnostics.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { Grid } from "../mui/Grid.js";
|
import { Grid } from "../mui/Grid.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
export function DeveloperPage(): VNode {
|
export function DeveloperPage(): VNode {
|
||||||
const [status, timedOut] = useDiagnostics();
|
const [status, timedOut] = useDiagnostics();
|
||||||
@ -45,13 +45,15 @@ export function DeveloperPage(): VNode {
|
|||||||
//FIXME: waiting for retry notification make a always increasing loop of notifications
|
//FIXME: waiting for retry notification make a always increasing loop of notifications
|
||||||
listenAllEvents.includes = (e) => e !== "waiting-for-retry"; // includes every event
|
listenAllEvents.includes = (e) => e !== "waiting-for-retry"; // includes every event
|
||||||
|
|
||||||
|
const api = useBackendContext();
|
||||||
|
|
||||||
const response = useAsyncAsHook(async () => {
|
const response = useAsyncAsHook(async () => {
|
||||||
const op = await wxApi.wallet.call(
|
const op = await api.wallet.call(
|
||||||
WalletApiOperation.GetPendingOperations,
|
WalletApiOperation.GetPendingOperations,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
const c = await wxApi.wallet.call(WalletApiOperation.DumpCoins, {});
|
const c = await api.wallet.call(WalletApiOperation.DumpCoins, {});
|
||||||
const ex = await wxApi.wallet.call(WalletApiOperation.ListExchanges, {});
|
const ex = await api.wallet.call(WalletApiOperation.ListExchanges, {});
|
||||||
return {
|
return {
|
||||||
operations: op.pendingOperations,
|
operations: op.pendingOperations,
|
||||||
coins: c.coins,
|
coins: c.coins,
|
||||||
@ -60,10 +62,7 @@ export function DeveloperPage(): VNode {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return wxApi.listener.onUpdateNotification(
|
return api.listener.onUpdateNotification(listenAllEvents, response?.retry);
|
||||||
listenAllEvents,
|
|
||||||
response?.retry,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const nonResponse = { operations: [], coins: [], exchanges: [] };
|
const nonResponse = { operations: [], coins: [], exchanges: [] };
|
||||||
@ -82,7 +81,7 @@ export function DeveloperPage(): VNode {
|
|||||||
coins={coins}
|
coins={coins}
|
||||||
exchanges={exchanges}
|
exchanges={exchanges}
|
||||||
onDownloadDatabase={async () => {
|
onDownloadDatabase={async () => {
|
||||||
const db = await wxApi.wallet.call(WalletApiOperation.ExportDb, {});
|
const db = await api.wallet.call(WalletApiOperation.ExportDb, {});
|
||||||
return JSON.stringify(db);
|
return JSON.stringify(db);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -135,9 +134,10 @@ export function View({
|
|||||||
content,
|
content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const api = useBackendContext();
|
||||||
const fileRef = useRef<HTMLInputElement>(null);
|
const fileRef = useRef<HTMLInputElement>(null);
|
||||||
async function onImportDatabase(str: string): Promise<void> {
|
async function onImportDatabase(str: string): Promise<void> {
|
||||||
return wxApi.wallet.call(WalletApiOperation.ImportDb, {
|
return api.wallet.call(WalletApiOperation.ImportDb, {
|
||||||
dump: JSON.parse(str),
|
dump: JSON.parse(str),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ export function View({
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
confirmReset(
|
confirmReset(
|
||||||
i18n.str`Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL YOUR COINS?`,
|
i18n.str`Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL YOUR COINS?`,
|
||||||
() => wxApi.background.resetDb(),
|
() => api.background.resetDb(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -190,7 +190,7 @@ export function View({
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
confirmReset(
|
confirmReset(
|
||||||
i18n.str`TESTING: This may delete all your coin, proceed with caution`,
|
i18n.str`TESTING: This may delete all your coin, proceed with caution`,
|
||||||
() => wxApi.background.runGarbageCollector(),
|
() => api.background.runGarbageCollector(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -55,6 +54,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const ComponentName = compose(
|
export const ComponentName = compose(
|
||||||
"ComponentName",
|
"ComponentName",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -14,10 +14,9 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState({ p }: Props, api: typeof wxApi): State {
|
export function useComponentState({ p }: Props): State {
|
||||||
return {
|
return {
|
||||||
status: "ready",
|
status: "ready",
|
||||||
error: undefined,
|
error: undefined,
|
||||||
|
@ -21,9 +21,9 @@ import {
|
|||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { h, VNode } from "preact";
|
import { h, VNode } from "preact";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { queryToSlashKeys } from "../utils/index.js";
|
import { queryToSlashKeys } from "../utils/index.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
import { ExchangeAddConfirmPage } from "./ExchangeAddConfirm.js";
|
import { ExchangeAddConfirmPage } from "./ExchangeAddConfirm.js";
|
||||||
import { ExchangeSetUrlPage } from "./ExchangeSetUrl.js";
|
import { ExchangeSetUrlPage } from "./ExchangeSetUrl.js";
|
||||||
|
|
||||||
@ -37,8 +37,9 @@ export function ExchangeAddPage({ currency, onBack }: Props): VNode {
|
|||||||
{ url: string; config: TalerConfigResponse } | undefined
|
{ url: string; config: TalerConfigResponse } | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
|
const api = useBackendContext();
|
||||||
const knownExchangesResponse = useAsyncAsHook(() =>
|
const knownExchangesResponse = useAsyncAsHook(() =>
|
||||||
wxApi.wallet.call(WalletApiOperation.ListExchanges, {}),
|
api.wallet.call(WalletApiOperation.ListExchanges, {}),
|
||||||
);
|
);
|
||||||
const knownExchanges = !knownExchangesResponse
|
const knownExchanges = !knownExchangesResponse
|
||||||
? []
|
? []
|
||||||
@ -75,7 +76,7 @@ export function ExchangeAddPage({ currency, onBack }: Props): VNode {
|
|||||||
url={verifying.url}
|
url={verifying.url}
|
||||||
onCancel={onBack}
|
onCancel={onBack}
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
await wxApi.wallet.call(WalletApiOperation.AddExchange, {
|
await api.wallet.call(WalletApiOperation.AddExchange, {
|
||||||
exchangeBaseUrl: canonicalizeBaseUrl(verifying.url),
|
exchangeBaseUrl: canonicalizeBaseUrl(verifying.url),
|
||||||
forceUpdate: true,
|
forceUpdate: true,
|
||||||
});
|
});
|
||||||
|
@ -18,14 +18,13 @@ import {
|
|||||||
DenomOperationMap,
|
DenomOperationMap,
|
||||||
ExchangeFullDetails,
|
ExchangeFullDetails,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
FeeDescriptionPair,
|
FeeDescriptionPair
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
||||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import {
|
import {
|
||||||
ComparingView,
|
ComparingView,
|
||||||
@ -33,7 +32,7 @@ import {
|
|||||||
NoExchangesView,
|
NoExchangesView,
|
||||||
PrivacyContentView,
|
PrivacyContentView,
|
||||||
ReadyView,
|
ReadyView,
|
||||||
TosContentView,
|
TosContentView
|
||||||
} from "./views.js";
|
} from "./views.js";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -106,6 +105,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const ExchangeSelectionPage = compose(
|
export const ExchangeSelectionPage = compose(
|
||||||
"ExchangeSelectionPage",
|
"ExchangeSelectionPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,17 +17,17 @@
|
|||||||
import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util";
|
import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
createPairTimeline,
|
createPairTimeline,
|
||||||
WalletApiOperation,
|
WalletApiOperation
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ onCancel, onSelection, list: exchanges, currentExchange }: Props,
|
{ onCancel, onSelection, list: exchanges, currentExchange }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const initialValue = exchanges.findIndex(
|
const initialValue = exchanges.findIndex(
|
||||||
(e) => e.exchangeBaseUrl === currentExchange,
|
(e) => e.exchangeBaseUrl === currentExchange,
|
||||||
);
|
);
|
||||||
@ -52,14 +52,14 @@ export function useComponentState(
|
|||||||
const selected = !selectedExchange
|
const selected = !selectedExchange
|
||||||
? undefined
|
? undefined
|
||||||
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
|
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
|
||||||
exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
|
exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
const original = !initialExchange
|
const original = !initialExchange
|
||||||
? undefined
|
? undefined
|
||||||
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
|
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
|
||||||
exchangeBaseUrl: initialExchange.exchangeBaseUrl,
|
exchangeBaseUrl: initialExchange.exchangeBaseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
exchanges,
|
exchanges,
|
||||||
|
@ -33,13 +33,13 @@ import {
|
|||||||
} from "../components/styled/index.js";
|
} from "../components/styled/index.js";
|
||||||
import { Time } from "../components/Time.js";
|
import { Time } from "../components/Time.js";
|
||||||
import { TransactionItem } from "../components/TransactionItem.js";
|
import { TransactionItem } from "../components/TransactionItem.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { NoBalanceHelp } from "../popup/NoBalanceHelp.js";
|
import { NoBalanceHelp } from "../popup/NoBalanceHelp.js";
|
||||||
import DownloadIcon from "../svg/download_24px.svg";
|
import DownloadIcon from "../svg/download_24px.svg";
|
||||||
import UploadIcon from "../svg/upload_24px.svg";
|
import UploadIcon from "../svg/upload_24px.svg";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currency?: string;
|
currency?: string;
|
||||||
@ -52,13 +52,14 @@ export function HistoryPage({
|
|||||||
goToWalletDeposit,
|
goToWalletDeposit,
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
const api = useBackendContext();
|
||||||
const state = useAsyncAsHook(async () => ({
|
const state = useAsyncAsHook(async () => ({
|
||||||
b: await wxApi.wallet.call(WalletApiOperation.GetBalances, {}),
|
b: await api.wallet.call(WalletApiOperation.GetBalances, {}),
|
||||||
tx: await wxApi.wallet.call(WalletApiOperation.GetTransactions, {}),
|
tx: await api.wallet.call(WalletApiOperation.GetTransactions, {}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return wxApi.listener.onUpdateNotification(
|
return api.listener.onUpdateNotification(
|
||||||
[NotificationType.WithdrawGroupFinished],
|
[NotificationType.WithdrawGroupFinished],
|
||||||
state?.retry,
|
state?.retry,
|
||||||
);
|
);
|
||||||
|
@ -20,10 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
|
|||||||
import {
|
import {
|
||||||
ButtonHandler,
|
ButtonHandler,
|
||||||
SelectFieldHandler,
|
SelectFieldHandler,
|
||||||
TextFieldHandler,
|
TextFieldHandler
|
||||||
} from "../../mui/handlers.js";
|
} from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
@ -75,6 +74,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const ManageAccountPage = compose(
|
export const ManageAccountPage = compose(
|
||||||
"ManageAccountPage",
|
"ManageAccountPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -17,18 +17,18 @@
|
|||||||
import {
|
import {
|
||||||
KnownBankAccountsInfo,
|
KnownBankAccountsInfo,
|
||||||
parsePaytoUri,
|
parsePaytoUri,
|
||||||
stringifyPaytoUri,
|
stringifyPaytoUri
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { AccountByType, Props, State } from "./index.js";
|
import { AccountByType, Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState(
|
export function useComponentState(
|
||||||
{ currency, onAccountAdded, onCancel }: Props,
|
{ currency, onAccountAdded, onCancel }: Props,
|
||||||
api: typeof wxApi,
|
|
||||||
): State {
|
): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const hook = useAsyncAsHook(() =>
|
const hook = useAsyncAsHook(() =>
|
||||||
api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
|
api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
|
||||||
);
|
);
|
||||||
|
@ -18,11 +18,10 @@ import { UserAttentionUnreadList } from "@gnu-taler/taler-util";
|
|||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { compose, StateViewMap } from "../../utils/index.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { useComponentState } from "./state.js";
|
import { useComponentState } from "./state.js";
|
||||||
import { LoadingUriView, ReadyView } from "./views.js";
|
import { LoadingUriView, ReadyView } from "./views.js";
|
||||||
|
|
||||||
export interface Props {}
|
export type Props = object
|
||||||
|
|
||||||
export type State = State.Loading | State.LoadingUriError | State.Ready;
|
export type State = State.Loading | State.LoadingUriError | State.Ready;
|
||||||
|
|
||||||
@ -56,6 +55,6 @@ const viewMapping: StateViewMap<State> = {
|
|||||||
|
|
||||||
export const NotificationsPage = compose(
|
export const NotificationsPage = compose(
|
||||||
"NotificationsPage",
|
"NotificationsPage",
|
||||||
(p: Props) => useComponentState(p, wxApi),
|
(p: Props) => useComponentState(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { useBackendContext } from "../../context/backend.js";
|
||||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||||
import { wxApi } from "../../wxApi.js";
|
|
||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState({}: Props, api: typeof wxApi): State {
|
export function useComponentState(p: Props): State {
|
||||||
|
const api = useBackendContext()
|
||||||
const hook = useAsyncAsHook(async () => {
|
const hook = useAsyncAsHook(async () => {
|
||||||
return await api.wallet.call(
|
return await api.wallet.call(
|
||||||
WalletApiOperation.GetUserAttentionRequests,
|
WalletApiOperation.GetUserAttentionRequests,
|
||||||
|
@ -31,10 +31,10 @@ import {
|
|||||||
SubTitle,
|
SubTitle,
|
||||||
Title,
|
Title,
|
||||||
} from "../components/styled/index.js";
|
} from "../components/styled/index.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { queryToSlashConfig } from "../utils/index.js";
|
import { queryToSlashConfig } from "../utils/index.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currency: string;
|
currency: string;
|
||||||
@ -46,7 +46,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
|
|||||||
| { url: string; name: string; provider: BackupBackupProviderTerms }
|
| { url: string; name: string; provider: BackupBackupProviderTerms }
|
||||||
| undefined
|
| undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
const api = useBackendContext();
|
||||||
if (!verifying) {
|
if (!verifying) {
|
||||||
return (
|
return (
|
||||||
<SetUrlView
|
<SetUrlView
|
||||||
@ -70,7 +70,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
|
|||||||
setVerifying(undefined);
|
setVerifying(undefined);
|
||||||
}}
|
}}
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
return wxApi.wallet
|
return api.wallet
|
||||||
.call(WalletApiOperation.AddBackupProvider, {
|
.call(WalletApiOperation.AddBackupProvider, {
|
||||||
backupProviderBaseUrl: verifying.url,
|
backupProviderBaseUrl: verifying.url,
|
||||||
name: verifying.name,
|
name: verifying.name,
|
||||||
|
@ -28,10 +28,10 @@ import { Loading } from "../components/Loading.js";
|
|||||||
import { LoadingError } from "../components/LoadingError.js";
|
import { LoadingError } from "../components/LoadingError.js";
|
||||||
import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
|
import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
|
||||||
import { Time } from "../components/Time.js";
|
import { Time } from "../components/Time.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pid: string;
|
pid: string;
|
||||||
@ -47,12 +47,10 @@ export function ProviderDetailPage({
|
|||||||
onWithdraw,
|
onWithdraw,
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
const api = useBackendContext();
|
||||||
async function getProviderInfo(): Promise<ProviderInfo | null> {
|
async function getProviderInfo(): Promise<ProviderInfo | null> {
|
||||||
//create a first list of backup info by currency
|
//create a first list of backup info by currency
|
||||||
const status = await wxApi.wallet.call(
|
const status = await api.wallet.call(WalletApiOperation.GetBackupInfo, {});
|
||||||
WalletApiOperation.GetBackupInfo,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const providers = status.providers.filter(
|
const providers = status.providers.filter(
|
||||||
(p) => p.syncProviderBaseUrl === providerURL,
|
(p) => p.syncProviderBaseUrl === providerURL,
|
||||||
@ -103,7 +101,7 @@ export function ProviderDetailPage({
|
|||||||
<ProviderView
|
<ProviderView
|
||||||
info={info}
|
info={info}
|
||||||
onSync={async () =>
|
onSync={async () =>
|
||||||
wxApi.wallet
|
api.wallet
|
||||||
.call(WalletApiOperation.RunBackupCycle, {
|
.call(WalletApiOperation.RunBackupCycle, {
|
||||||
providers: [providerURL],
|
providers: [providerURL],
|
||||||
})
|
})
|
||||||
@ -120,7 +118,7 @@ export function ProviderDetailPage({
|
|||||||
onWithdraw(info.paymentStatus.amount);
|
onWithdraw(info.paymentStatus.amount);
|
||||||
}}
|
}}
|
||||||
onDelete={() =>
|
onDelete={() =>
|
||||||
wxApi.wallet
|
api.wallet
|
||||||
.call(WalletApiOperation.RemoveBackupProvider, {
|
.call(WalletApiOperation.RemoveBackupProvider, {
|
||||||
provider: providerURL,
|
provider: providerURL,
|
||||||
})
|
})
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
SuccessText,
|
SuccessText,
|
||||||
WarningText,
|
WarningText,
|
||||||
} from "../components/styled/index.js";
|
} from "../components/styled/index.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useDevContext } from "../context/devContext.js";
|
import { useDevContext } from "../context/devContext.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
@ -43,7 +44,6 @@ import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js";
|
|||||||
import { ToggleHandler } from "../mui/handlers.js";
|
import { ToggleHandler } from "../mui/handlers.js";
|
||||||
import { Pages } from "../NavigationBar.js";
|
import { Pages } from "../NavigationBar.js";
|
||||||
import { platform } from "../platform/api.js";
|
import { platform } from "../platform/api.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
|
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
|
||||||
|
|
||||||
@ -53,10 +53,11 @@ export function SettingsPage(): VNode {
|
|||||||
const { devModeToggle } = useDevContext();
|
const { devModeToggle } = useDevContext();
|
||||||
const { name, update } = useBackupDeviceName();
|
const { name, update } = useBackupDeviceName();
|
||||||
const webex = platform.getWalletWebExVersion();
|
const webex = platform.getWalletWebExVersion();
|
||||||
|
const api = useBackendContext();
|
||||||
|
|
||||||
const exchangesHook = useAsyncAsHook(async () => {
|
const exchangesHook = useAsyncAsHook(async () => {
|
||||||
const list = await wxApi.wallet.call(WalletApiOperation.ListExchanges, {});
|
const list = await api.wallet.call(WalletApiOperation.ListExchanges, {});
|
||||||
const version = await wxApi.wallet.call(WalletApiOperation.GetVersion, {});
|
const version = await api.wallet.call(WalletApiOperation.GetVersion, {});
|
||||||
return { exchanges: list.exchanges, version };
|
return { exchanges: list.exchanges, version };
|
||||||
});
|
});
|
||||||
const { exchanges, version } =
|
const { exchanges, version } =
|
||||||
|
@ -60,11 +60,11 @@ import {
|
|||||||
WarningBox,
|
WarningBox,
|
||||||
} from "../components/styled/index.js";
|
} from "../components/styled/index.js";
|
||||||
import { Time } from "../components/Time.js";
|
import { Time } from "../components/Time.js";
|
||||||
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { useTranslationContext } from "../context/translation.js";
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||||
import { Button } from "../mui/Button.js";
|
import { Button } from "../mui/Button.js";
|
||||||
import { Pages } from "../NavigationBar.js";
|
import { Pages } from "../NavigationBar.js";
|
||||||
import { wxApi } from "../wxApi.js";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tid: string;
|
tid: string;
|
||||||
@ -76,17 +76,17 @@ export function TransactionPage({
|
|||||||
goToWalletHistory,
|
goToWalletHistory,
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
const api = useBackendContext();
|
||||||
const state = useAsyncAsHook(
|
const state = useAsyncAsHook(
|
||||||
() =>
|
() =>
|
||||||
wxApi.wallet.call(WalletApiOperation.GetTransactionById, {
|
api.wallet.call(WalletApiOperation.GetTransactionById, {
|
||||||
transactionId,
|
transactionId,
|
||||||
}),
|
}),
|
||||||
[transactionId],
|
[transactionId],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
wxApi.listener.onUpdateNotification(
|
api.listener.onUpdateNotification(
|
||||||
[NotificationType.WithdrawGroupFinished],
|
[NotificationType.WithdrawGroupFinished],
|
||||||
state?.retry,
|
state?.retry,
|
||||||
),
|
),
|
||||||
@ -118,19 +118,19 @@ export function TransactionPage({
|
|||||||
null;
|
null;
|
||||||
}}
|
}}
|
||||||
onDelete={async () => {
|
onDelete={async () => {
|
||||||
await wxApi.wallet.call(WalletApiOperation.DeleteTransaction, {
|
await api.wallet.call(WalletApiOperation.DeleteTransaction, {
|
||||||
transactionId,
|
transactionId,
|
||||||
});
|
});
|
||||||
goToWalletHistory(currency);
|
goToWalletHistory(currency);
|
||||||
}}
|
}}
|
||||||
onRetry={async () => {
|
onRetry={async () => {
|
||||||
await wxApi.wallet.call(WalletApiOperation.RetryTransaction, {
|
await api.wallet.call(WalletApiOperation.RetryTransaction, {
|
||||||
transactionId,
|
transactionId,
|
||||||
});
|
});
|
||||||
goToWalletHistory(currency);
|
goToWalletHistory(currency);
|
||||||
}}
|
}}
|
||||||
onRefund={async (purchaseId) => {
|
onRefund={async (purchaseId) => {
|
||||||
await wxApi.wallet.call(WalletApiOperation.ApplyRefundFromPurchaseId, {
|
await api.wallet.call(WalletApiOperation.ApplyRefundFromPurchaseId, {
|
||||||
purchaseId,
|
purchaseId,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -151,6 +151,14 @@ function onUpdateNotification(
|
|||||||
return platform.listenToWalletBackground(onNewMessage);
|
return platform.listenToWalletBackground(onNewMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WxApiType = {
|
||||||
|
wallet: WalletCoreApiClient;
|
||||||
|
background: BackgroundApiClient;
|
||||||
|
listener: {
|
||||||
|
onUpdateNotification: typeof onUpdateNotification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const wxApi = {
|
export const wxApi = {
|
||||||
wallet: new WxWalletCoreApiClient(),
|
wallet: new WxWalletCoreApiClient(),
|
||||||
background: new BackgroundApiClient(),
|
background: new BackgroundApiClient(),
|
||||||
|
@ -246,7 +246,7 @@ interface HookTestResultError {
|
|||||||
export async function hookBehaveLikeThis<T extends object, PropsType>(
|
export async function hookBehaveLikeThis<T extends object, PropsType>(
|
||||||
hookFunction: (p: PropsType) => RecursiveState<T>,
|
hookFunction: (p: PropsType) => RecursiveState<T>,
|
||||||
props: PropsType,
|
props: PropsType,
|
||||||
checks: Array<(state: T) => void>,
|
checks: Array<(state: Exclude<T, VoidFunction>) => void>,
|
||||||
Context?: ({ children }: { children: any }) => VNode | null,
|
Context?: ({ children }: { children: any }) => VNode | null,
|
||||||
): Promise<HookTestResult> {
|
): Promise<HookTestResult> {
|
||||||
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
|
||||||
|
Loading…
Reference in New Issue
Block a user