summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet/AddBackupProvider
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/AddBackupProvider')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts27
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts117
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx21
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts75
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx18
5 files changed, 113 insertions, 145 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
index 3205588af..daa6b425d 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
@@ -15,29 +15,22 @@
*/
import {
- AmountJson,
- BackupBackupProviderTerms,
+ SyncTermsOfServiceResponse,
TalerErrorDetail,
} from "@gnu-taler/taler-util";
-import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
import {
ButtonHandler,
TextFieldHandler,
ToggleHandler,
} from "../../mui/handlers.js";
-import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
+import { StateViewMap, compose } from "../../utils/index.js";
import { useComponentState } from "./state.js";
-import {
- LoadingUriView,
- SelectProviderView,
- ConfirmProviderView,
-} from "./views.js";
+import { ConfirmProviderView, SelectProviderView } from "./views.js";
export interface Props {
- currency: string;
onBack: () => Promise<void>;
onComplete: (pid: string) => Promise<void>;
onPaymentRequired: (uri: string) => Promise<void>;
@@ -56,13 +49,13 @@ export namespace State {
}
export interface LoadingUriError {
- status: "loading-error";
- error: HookError;
+ status: "error";
+ error: ErrorAlert;
}
export interface ConfirmProvider {
status: "confirm-provider";
- error: undefined | TalerErrorDetail;
+ error: undefined;
url: string;
provider: SyncTermsOfServiceResponse;
tos: ToggleHandler;
@@ -83,13 +76,13 @@ export namespace State {
const viewMapping: StateViewMap<State> = {
loading: Loading,
- "loading-error": LoadingUriView,
+ error: ErrorAlertView,
"select-provider": SelectProviderView,
"confirm-provider": ConfirmProviderView,
};
export const AddBackupProviderPage = compose(
"AddBackupProvider",
- (p: Props) => useComponentState(p, wxApi),
+ (p: Props) => useComponentState(p),
viewMapping,
);
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
index 504ee4678..75b8e53c0 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
@@ -17,15 +17,13 @@
import {
canonicalizeBaseUrl,
Codec,
- TalerErrorDetail,
-} from "@gnu-taler/taler-util";
-import {
codecForSyncTermsOfServiceResponse,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useEffect, useState } from "preact/hooks";
+import { useAlertContext } from "../../context/alert.js";
+import { useBackendContext } from "../../context/backend.js";
import { assertUnreachable } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
import { Props, State } from "./index.js";
type UrlState<T> = UrlOk<T> | UrlError;
@@ -101,66 +99,69 @@ function useUrlState<T>(
}
const constHref = href;
- useDebounceEffect(
- 500,
- constHref == undefined
- ? undefined
- : async () => {
- const req = await fetch(constHref).catch((e) => {
- return setState({
- status: "network-error",
- href: constHref,
- });
- });
- if (!req) return;
+ async function checkURL() {
+ if (!constHref) {
+ return;
+ }
+ const req = await fetch(constHref).catch((e) => {
+ return setState({
+ status: "network-error",
+ href: constHref,
+ });
+ });
+ if (!req) return;
- if (req.status >= 400 && req.status < 500) {
- setState({
- status: "client-error",
- code: req.status,
- });
- return;
- }
- if (req.status > 500) {
- setState({
- status: "server-error",
- code: req.status,
- });
- return;
- }
+ if (req.status >= 400 && req.status < 500) {
+ setState({
+ status: "client-error",
+ code: req.status,
+ });
+ return;
+ }
+ if (req.status > 500) {
+ setState({
+ status: "server-error",
+ code: req.status,
+ });
+ return;
+ }
- const json = await req.json();
- try {
- const result = codec.decode(json);
- setState({ status: "ok", result });
- } catch (e: any) {
- setState({ status: "parsing-error", json });
- }
- },
+ const json = await req.json();
+ try {
+ const result = codec.decode(json);
+ setState({ status: "ok", result });
+ } catch (e: any) {
+ setState({ status: "parsing-error", json });
+ }
+ }
+
+ useDebounceEffect(
+ 500,
+ constHref == undefined ? undefined : checkURL,
[host, path],
);
return state;
}
-export function useComponentState(
- { currency, onBack, onComplete, onPaymentRequired }: Props,
- api: typeof wxApi,
-): State {
+export function useComponentState({
+ onBack,
+ onComplete,
+ onPaymentRequired,
+}: Props): State {
+ const api = useBackendContext();
const [url, setHost] = useState<string | undefined>();
const [name, setName] = useState<string | undefined>();
const [tos, setTos] = useState(false);
+ const { pushAlertOnError } = useAlertContext();
const urlState = useUrlState(
url,
"config",
codecForSyncTermsOfServiceResponse(),
);
- const [operationError, setOperationError] = useState<
- TalerErrorDetail | undefined
- >();
const [showConfirm, setShowConfirm] = useState(false);
- async function addBackupProvider() {
+ async function addBackupProvider(): Promise<void> {
if (!url || !name) return;
const resp = await api.wallet.call(WalletApiOperation.AddBackupProvider, {
@@ -176,8 +177,6 @@ export function useComponentState(
} else {
return onComplete(url);
}
- case "error":
- return setOperationError(resp.error);
case "ok":
return onComplete(url);
default:
@@ -188,18 +187,18 @@ export function useComponentState(
if (showConfirm && urlState && urlState.status === "ok") {
return {
status: "confirm-provider",
- error: operationError,
+ error: undefined,
onAccept: {
- onClick: !tos ? undefined : addBackupProvider,
+ onClick: !tos ? undefined : pushAlertOnError(addBackupProvider),
},
onCancel: {
- onClick: onBack,
+ onClick: pushAlertOnError(onBack),
},
provider: urlState.result,
tos: {
value: tos,
button: {
- onClick: async () => setTos(!tos),
+ onClick: pushAlertOnError(async () => setTos(!tos)),
},
},
url: url ?? "",
@@ -211,25 +210,25 @@ export function useComponentState(
error: undefined,
name: {
value: name || "",
- onInput: async (e) => setName(e),
+ onInput: pushAlertOnError(async (e) => setName(e)),
error:
name === undefined ? undefined : !name ? "Can't be empty" : undefined,
},
onCancel: {
- onClick: onBack,
+ onClick: pushAlertOnError(onBack),
},
onConfirm: {
onClick:
!urlState || urlState.status !== "ok" || !name
? undefined
- : async () => {
+ : pushAlertOnError(async () => {
setShowConfirm(true);
- },
+ }),
},
urlOk: urlState?.status === "ok",
url: {
value: url || "",
- onInput: async (e) => setHost(e),
+ onInput: pushAlertOnError(async (e) => setHost(e)),
error: errorString(urlState),
},
};
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
index 887ad235e..7ac92c6c9 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
@@ -19,17 +19,18 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { createExample } from "../../test-utils.js";
+import * as tests from "@gnu-taler/web-util/testing";
import { ConfirmProviderView, SelectProviderView } from "./views.js";
+import { AmountString } from "@gnu-taler/taler-util";
export default {
title: "add backup provider",
};
-export const DemoService = createExample(ConfirmProviderView, {
+export const DemoService = tests.createExample(ConfirmProviderView, {
url: "https://sync.demo.taler.net/",
provider: {
- annual_fee: "KUDOS:0.1",
+ annual_fee: "KUDOS:0.1" as AmountString,
storage_limit_in_megabytes: 20,
version: "1",
},
@@ -40,10 +41,10 @@ export const DemoService = createExample(ConfirmProviderView, {
onCancel: {},
});
-export const FreeService = createExample(ConfirmProviderView, {
+export const FreeService = tests.createExample(ConfirmProviderView, {
url: "https://sync.taler:9667/",
provider: {
- annual_fee: "ARS:0",
+ annual_fee: "ARS:0" as AmountString,
storage_limit_in_megabytes: 20,
version: "1",
},
@@ -54,14 +55,14 @@ export const FreeService = createExample(ConfirmProviderView, {
onCancel: {},
});
-export const Initial = createExample(SelectProviderView, {
+export const Initial = tests.createExample(SelectProviderView, {
url: { value: "" },
name: { value: "" },
onCancel: {},
onConfirm: {},
});
-export const WithValue = createExample(SelectProviderView, {
+export const WithValue = tests.createExample(SelectProviderView, {
url: {
value: "sync.demo.taler.net",
},
@@ -72,7 +73,7 @@ export const WithValue = createExample(SelectProviderView, {
onConfirm: {},
});
-export const WithConnectionError = createExample(SelectProviderView, {
+export const WithConnectionError = tests.createExample(SelectProviderView, {
url: {
value: "sync.demo.taler.net",
error: "Network error",
@@ -84,7 +85,7 @@ export const WithConnectionError = createExample(SelectProviderView, {
onConfirm: {},
});
-export const WithClientError = createExample(SelectProviderView, {
+export const WithClientError = tests.createExample(SelectProviderView, {
url: {
value: "sync.demo.taler.net",
error: "URL may not be right: (404) Not Found",
@@ -96,7 +97,7 @@ export const WithClientError = createExample(SelectProviderView, {
onConfirm: {},
});
-export const WithServerError = createExample(SelectProviderView, {
+export const WithServerError = tests.createExample(SelectProviderView, {
url: {
value: "sync.demo.taler.net",
error: "Try another server: (500) Internal Server Error",
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
index 1143853f8..058f4f460 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
@@ -19,61 +19,50 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai";
-import {
- createWalletApiMock,
- mountHook,
- nullFunction,
-} from "../../test-utils.js";
+import * as tests from "@gnu-taler/web-util/testing";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
import { Props } from "./index.js";
import { useComponentState } from "./state.js";
const props: Props = {
- currency: "KUDOS",
onBack: nullFunction,
onComplete: nullFunction,
onPaymentRequired: nullFunction,
};
describe("AddBackupProvider states", () => {
- it("should start in 'select-provider' state", async () => {
- const { handler, mock } = createWalletApiMock();
+ /**
+ * FIXME: this test has inconsistent behavior.
+ * it should always expect one state but for some reason
+ * (maybe race condition) it sometime expect 1 update when
+ * it should no update
+ */
+ it.skip("should start in 'select-provider' state", async () => {
+ const { handler, TestingContext } = createWalletApiMock();
- // handler.addWalletCallResponse(
- // WalletApiOperation.ListKnownBankAccounts,
- // undefined,
- // {
- // accounts: [],
- // },
- // );
+ const hookBehavior = await tests.hookBehaveLikeThis(
+ useComponentState,
+ props,
+ [
+ (state) => {
+ expect(state.status).equal("select-provider");
+ if (state.status !== "select-provider") return;
+ expect(state.name.value).eq("");
+ expect(state.url.value).eq("");
+ },
+ //FIXME: this shouldn't take 2 updates, just
+ // (state) => {
+ // expect(state.status).equal("select-provider");
+ // if (state.status !== "select-provider") return;
+ // expect(state.name.value).eq("");
+ // expect(state.url.value).eq("");
+ // },
+ ],
+ TestingContext,
+ );
- const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
- 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(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
});
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
index b633a595f..c67c288dc 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
@@ -17,30 +17,17 @@
import { Amounts } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../../components/Checkbox.js";
-import { LoadingError } from "../../components/LoadingError.js";
import {
LightText,
SmallLightText,
SubTitle,
- TermsOfService,
Title,
} from "../../components/styled/index.js";
-import { useTranslationContext } from "../../context/translation.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Button } from "../../mui/Button.js";
import { TextField } from "../../mui/TextField.js";
import { State } from "./index.js";
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
- const { i18n } = useTranslationContext();
-
- return (
- <LoadingError
- title={<i18n.Translate>Could not load</i18n.Translate>}
- error={error}
- />
- );
-}
-
export function ConfirmProviderView({
url,
provider,
@@ -88,9 +75,8 @@ export function ConfirmProviderView({
of service
</i18n.Translate>
</p>
- {/* replace with <TermsOfService /> */}
<Checkbox
- label={<i18n.Translate>Accept terms of service</i18n.Translate>}
+ label={i18n.str`Accept terms of service`}
name="terms"
onToggle={tos.button.onClick}
enabled={tos.value}