aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-02 00:41:34 -0300
committerSebastian <sebasjm@gmail.com>2024-04-02 00:41:34 -0300
commit026d9cacaec62fe6c853b3f716a98e6e82e1dd19 (patch)
treeabb5b1344f01757c2f8f656c0e8b4349509c4915 /packages/taler-wallet-webextension
parent38b4e3cc557bf83cc8e07288cc9d405f252a8d61 (diff)
downloadwallet-core-026d9cacaec62fe6c853b3f716a98e6e82e1dd19.tar.xz
fix #8693
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/src/NavigationBar.tsx3
-rw-r--r--packages/taler-wallet-webextension/src/cta/DevExperiment/index.ts73
-rw-r--r--packages/taler-wallet-webextension/src/cta/DevExperiment/state.ts83
-rw-r--r--packages/taler-wallet-webextension/src/cta/DevExperiment/stories.tsx33
-rw-r--r--packages/taler-wallet-webextension/src/cta/DevExperiment/test.ts65
-rw-r--r--packages/taler-wallet-webextension/src/cta/DevExperiment/views.tsx74
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Application.tsx13
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx131
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts1
9 files changed, 446 insertions, 30 deletions
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index 78a526997..fc917088d 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -127,6 +127,7 @@ export const Pages = {
ctaRefund: "/cta/refund",
ctaWithdraw: "/cta/withdraw",
ctaDeposit: "/cta/deposit",
+ ctaExperiment: "/cta/experiment",
ctaInvoiceCreate: pageDefinition<{ amount?: string }>(
"/cta/invoice/create/:amount?",
),
@@ -151,7 +152,7 @@ const talerUriActionToPageName: {
[TalerUriAction.Restore]: "ctaRecovery",
[TalerUriAction.PayTemplate]: "ctaPayTemplate",
[TalerUriAction.WithdrawExchange]: "ctaWithdrawManual",
- [TalerUriAction.DevExperiment]: undefined,
+ [TalerUriAction.DevExperiment]: "ctaExperiment",
};
export function getPathnameForTalerURI(talerUri: string): string | undefined {
diff --git a/packages/taler-wallet-webextension/src/cta/DevExperiment/index.ts b/packages/taler-wallet-webextension/src/cta/DevExperiment/index.ts
new file mode 100644
index 000000000..ec09fd9f1
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/DevExperiment/index.ts
@@ -0,0 +1,73 @@
+/*
+ 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/>
+ */
+
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
+import { Loading } from "../../components/Loading.js";
+import { ErrorAlert } from "../../context/alert.js";
+import { ButtonHandler } from "../../mui/handlers.js";
+import { StateViewMap, compose } from "../../utils/index.js";
+import { useComponentState } from "./state.js";
+import { InsertLostView, InsertPendingRefreshView, UnknownView } from "./views.js";
+
+export interface Props {
+ talerExperimentUri: string | undefined;
+ onCancel: () => Promise<void>;
+ onSuccess: () => Promise<void>;
+}
+
+export type State = State.Loading | State.LoadingUriError | State.Unknown | State.InsertLost | State.PendingRefresh;
+
+export namespace State {
+ export interface Loading {
+ status: "loading";
+ error: undefined;
+ }
+ export interface LoadingUriError {
+ status: "error";
+ error: ErrorAlert;
+ }
+ export interface InsertLost {
+ status: "insertLost";
+ error: undefined;
+ confirm: ButtonHandler;
+ cancel: () => Promise<void>;
+ }
+ export interface PendingRefresh {
+ status: "pendingRefresh";
+ error: undefined;
+ confirm: ButtonHandler;
+ cancel: () => Promise<void>;
+ }
+ export interface Unknown {
+ status: "unknown";
+ experimentId: string;
+ error: undefined;
+ }
+}
+
+const viewMapping: StateViewMap<State> = {
+ loading: Loading,
+ error: ErrorAlertView,
+ pendingRefresh: InsertPendingRefreshView,
+ insertLost: InsertLostView,
+ unknown: UnknownView,
+};
+
+export const DevExperimentPage = compose(
+ "DevExperiment",
+ (p: Props) => useComponentState(p),
+ viewMapping,
+);
diff --git a/packages/taler-wallet-webextension/src/cta/DevExperiment/state.ts b/packages/taler-wallet-webextension/src/cta/DevExperiment/state.ts
new file mode 100644
index 000000000..774a1129d
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/DevExperiment/state.ts
@@ -0,0 +1,83 @@
+/*
+ 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/>
+ */
+
+import { parseDevExperimentUri } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useAlertContext } from "../../context/alert.js";
+import { useBackendContext } from "../../context/backend.js";
+import { Props, State } from "./index.js";
+
+export function useComponentState({
+ talerExperimentUri,
+ onCancel,
+ onSuccess,
+}: Props): State {
+ const api = useBackendContext();
+ const { pushAlertOnError } = useAlertContext();
+ const { i18n } = useTranslationContext();
+
+ async function doApply(): Promise<void> {
+ if (!talerExperimentUri) return;
+ await api.wallet.call(WalletApiOperation.ApplyDevExperiment, {
+ devExperimentUri: talerExperimentUri
+ })
+ // const resp = await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
+ // amount: Amounts.stringify(amount),
+ // depositPaytoUri: uri,
+ // });
+ onSuccess();
+ }
+ const uri = talerExperimentUri === undefined ? undefined : parseDevExperimentUri(talerExperimentUri);
+
+ if (!uri) {
+ return {
+ status: "error",
+ error: {
+ type: "error",
+ message: i18n.str`Invalid dev experiment URI.`,
+ description: i18n.str`URI: ${talerExperimentUri}`,
+ cause: {},
+ context: {},
+ },
+ };
+ }
+ if (uri.devExperimentId === "insert-denom-loss") {
+ return {
+ status: "insertLost",
+ error: undefined,
+ confirm: {
+ onClick: pushAlertOnError(doApply),
+ },
+ cancel: onCancel,
+ };
+ }
+ if (uri.devExperimentId === "insert-pending-refresh") {
+ return {
+ status: "pendingRefresh",
+ error: undefined,
+ confirm: {
+ onClick: pushAlertOnError(doApply),
+ },
+ cancel: onCancel,
+ };
+ }
+ return {
+ status: "unknown",
+ error: undefined,
+ experimentId: uri.devExperimentId,
+ }
+}
diff --git a/packages/taler-wallet-webextension/src/cta/DevExperiment/stories.tsx b/packages/taler-wallet-webextension/src/cta/DevExperiment/stories.tsx
new file mode 100644
index 000000000..c9851495f
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/DevExperiment/stories.tsx
@@ -0,0 +1,33 @@
+/*
+ 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 * as tests from "@gnu-taler/web-util/testing";
+import { InsertLostView } from "./views.js";
+
+export default {
+ title: "dev-experiment",
+};
+
+export const Ready = tests.createExample(InsertLostView, {
+ status: "insertLost",
+ confirm: {},
+ error: undefined,
+});
diff --git a/packages/taler-wallet-webextension/src/cta/DevExperiment/test.ts b/packages/taler-wallet-webextension/src/cta/DevExperiment/test.ts
new file mode 100644
index 000000000..d4f2ca8b1
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/DevExperiment/test.ts
@@ -0,0 +1,65 @@
+/*
+ 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 * as tests from "@gnu-taler/web-util/testing";
+import { expect } from "chai";
+import { createWalletApiMock } from "../../test-utils.js";
+import { Props } from "./index.js";
+import { useComponentState } from "./state.js";
+
+describe("DevExperiment CTA states", () => {
+ it("should tell the user that the URI is missing", async () => {
+ const { handler, TestingContext } = createWalletApiMock();
+
+ const props: Props = {
+ talerExperimentUri: undefined,
+ onCancel: async () => {
+ null;
+ },
+ onSuccess: async () => {
+ null;
+ },
+ };
+
+ const hookBehavior = await tests.hookBehaveLikeThis(
+ useComponentState,
+ props,
+ [
+ ({ status }) => {
+ expect(status).equals("error");
+ },
+ ({ status, error }) => {
+ expect(status).equals("error");
+
+ if (!error) expect.fail();
+ // if (!error.hasError) expect.fail();
+ // if (error.operational) expect.fail();
+ // expect(error.description).eq("ERROR_NO-URI-FOR-DEPOSIT");
+ },
+ ],
+ TestingContext,
+ );
+
+ expect(hookBehavior).deep.equal({ result: "ok" });
+ expect(handler.getCallingQueueState()).eq("empty");
+ });
+
+});
diff --git a/packages/taler-wallet-webextension/src/cta/DevExperiment/views.tsx b/packages/taler-wallet-webextension/src/cta/DevExperiment/views.tsx
new file mode 100644
index 000000000..afad17ad1
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/DevExperiment/views.tsx
@@ -0,0 +1,74 @@
+/*
+ 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/>
+ */
+
+import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { Amount } from "../../components/Amount.js";
+import { Part } from "../../components/Part.js";
+import { Button } from "../../mui/Button.js";
+import { State } from "./index.js";
+
+/**
+ *
+ * @author sebasjm
+ */
+
+export function InsertLostView(state: State.InsertLost): VNode {
+ const { i18n } = useTranslationContext();
+ return <Fragment>
+ <section>
+ <Part
+ title={i18n.str`Experiment`}
+ text={i18n.str`Insert lost denomination`}
+ />
+ </section>
+ <section>
+ <Button
+ variant="contained"
+ color="success"
+ onClick={state.confirm.onClick}
+ >
+ <i18n.Translate>Apply</i18n.Translate>
+ </Button>
+ </section>
+ </Fragment>
+}
+
+export function InsertPendingRefreshView(state: State.PendingRefresh): VNode {
+ const { i18n } = useTranslationContext();
+ return <Fragment>
+ <section>
+ <Part
+ title={i18n.str`Experiment`}
+ text={i18n.str`Pending refresh`}
+ />
+ </section>
+ <section>
+ <Button
+ variant="contained"
+ color="success"
+ onClick={state.confirm.onClick}
+ >
+ <i18n.Translate>Apply</i18n.Translate>
+ </Button>
+ </section>
+ </Fragment>
+}
+
+export function UnknownView(state: State.Unknown): VNode {
+ return <div>unknown experiment "{state.experimentId}"</div>
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 62a519f06..1fc1e46f4 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -83,6 +83,7 @@ import { TransactionPage } from "./Transaction.js";
import { WelcomePage } from "./Welcome.js";
import { WalletActivity } from "../components/WalletActivity.js";
import { EnabledBySettings } from "../components/EnabledBySettings.js";
+import { DevExperimentPage } from "../cta/DevExperiment/index.js";
export function Application(): VNode {
const { i18n } = useTranslationContext();
@@ -497,6 +498,18 @@ export function Application(): VNode {
</CallToActionTemplate>
)}
/>
+ <Route
+ path={Pages.ctaExperiment}
+ component={({ talerUri }: { talerUri: string }) => (
+ <CallToActionTemplate title={i18n.str`Development experiment`}>
+ <DevExperimentPage
+ talerExperimentUri={decodeURIComponent(talerUri)}
+ onCancel={() => redirectTo(Pages.balanceHistory({}))}
+ onSuccess={() => redirectTo(Pages.balanceHistory({}))}
+ />
+ </CallToActionTemplate>
+ )}
+ />
{/**
* NOT FOUND
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index a5fa66cbe..11fb88cf6 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -19,6 +19,7 @@ import {
AmountJson,
Amounts,
AmountString,
+ DenomLossEventType,
MerchantInfo,
NotificationType,
OrderShortInfo,
@@ -1027,36 +1028,108 @@ export function TransactionView({
}
if (transaction.type === TransactionType.DenomLoss) {
- return (
- <TransactionTemplate
- transaction={transaction}
- onDelete={onDelete}
- onRetry={onRetry}
- onAbort={onAbort}
- onResume={onResume}
- onSuspend={onSuspend}
- onCancel={onCancel}
- >
- <Header
- timestamp={transaction.timestamp}
- type={i18n.str`Debit`}
- total={effective}
- kind="negative"
- >
- <i18n.Translate>Lost</i18n.Translate>
- </Header>
+ switch (transaction.lossEventType) {
+ case DenomLossEventType.DenomExpired: {
+ return (
+ <TransactionTemplate
+ transaction={transaction}
+ onDelete={onDelete}
+ onRetry={onRetry}
+ onAbort={onAbort}
+ onResume={onResume}
+ onSuspend={onSuspend}
+ onCancel={onCancel}
+ >
+ <Header
+ timestamp={transaction.timestamp}
+ type={i18n.str`Debit`}
+ total={effective}
+ kind="negative"
+ >
+ <i18n.Translate>Lost</i18n.Translate>
+ </Header>
+
+ <Part
+ title={i18n.str`Exchange`}
+ text={transaction.exchangeBaseUrl as TranslatedString}
+ kind="neutral"
+ />
+ <Part
+ title={i18n.str`Reason`}
+ text={i18n.str`Denomination expired.`}
+ />
+ </TransactionTemplate>
+ );
+ }
+ case DenomLossEventType.DenomVanished: {
+ return (
+ <TransactionTemplate
+ transaction={transaction}
+ onDelete={onDelete}
+ onRetry={onRetry}
+ onAbort={onAbort}
+ onResume={onResume}
+ onSuspend={onSuspend}
+ onCancel={onCancel}
+ >
+ <Header
+ timestamp={transaction.timestamp}
+ type={i18n.str`Debit`}
+ total={effective}
+ kind="negative"
+ >
+ <i18n.Translate>Lost</i18n.Translate>
+ </Header>
+
+ <Part
+ title={i18n.str`Exchange`}
+ text={transaction.exchangeBaseUrl as TranslatedString}
+ kind="neutral"
+ />
+ <Part
+ title={i18n.str`Reason`}
+ text={i18n.str`Denomination vanished.`}
+ />
+ </TransactionTemplate>
+ );
+ }
+ case DenomLossEventType.DenomUnoffered: {
+ return (
+ <TransactionTemplate
+ transaction={transaction}
+ onDelete={onDelete}
+ onRetry={onRetry}
+ onAbort={onAbort}
+ onResume={onResume}
+ onSuspend={onSuspend}
+ onCancel={onCancel}
+ >
+ <Header
+ timestamp={transaction.timestamp}
+ type={i18n.str`Debit`}
+ total={effective}
+ kind="negative"
+ >
+ <i18n.Translate>Lost</i18n.Translate>
+ </Header>
+
+ <Part
+ title={i18n.str`Exchange`}
+ text={transaction.exchangeBaseUrl as TranslatedString}
+ kind="neutral"
+ />
+ <Part
+ title={i18n.str`Reason`}
+ text={i18n.str`Denomination is unoffered.`}
+ />
+ </TransactionTemplate>
+ );
+ }
+ default: {
+ assertUnreachable(transaction.lossEventType)
+ }
+ }
- <Part
- title={i18n.str`Exchange`}
- text={transaction.exchangeBaseUrl as TranslatedString}
- kind="neutral"
- />
- <Part
- title={i18n.str`Reason`}
- text={transaction.lossEventType as TranslatedString}
- />
- </TransactionTemplate>
- );
}
if (transaction.type === TransactionType.Recoup) {
throw Error("recoup transaction not implemented");
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index 44e4c0d48..a78e85aba 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -305,6 +305,7 @@ async function reinitWallet(): Promise<void> {
config: {
testing: {
emitObservabilityEvents: settings.showWalletActivity,
+ devModeActive: settings.advancedMode,
},
features: {
allowHttp: settings.walletAllowHttp,