summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/cta
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-03-10 01:27:31 -0300
committerSebastian <sebasjm@gmail.com>2023-03-10 01:27:31 -0300
commit867d2ca76b2ca8903b2263a68243899749de7011 (patch)
tree5abb027f7976a8694d29e5be4f8d9e8528bf1a0c /packages/taler-wallet-webextension/src/cta
parentf40487806304dbaafa74544d5a8f74ab56569044 (diff)
downloadwallet-core-867d2ca76b2ca8903b2263a68243899749de7011.tar.gz
wallet-core-867d2ca76b2ca8903b2263a68243899749de7011.tar.bz2
wallet-core-867d2ca76b2ca8903b2263a68243899749de7011.zip
fix encoded uri, add pay template cta
Diffstat (limited to 'packages/taler-wallet-webextension/src/cta')
-rw-r--r--packages/taler-wallet-webextension/src/cta/Payment/index.ts2
-rw-r--r--packages/taler-wallet-webextension/src/cta/Payment/test.ts2
-rw-r--r--packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts37
-rw-r--r--packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts128
-rw-r--r--packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts6
-rw-r--r--packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx57
7 files changed, 210 insertions, 24 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/index.ts b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
index e844c1706..c9bead89c 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
@@ -30,7 +30,7 @@ import { useComponentState } from "./state.js";
import { BaseView } from "./views.js";
export interface Props {
- talerPayUri?: string;
+ talerPayUri: string;
goToWalletManualWithdraw: (amount?: string) => Promise<void>;
cancel: () => Promise<void>;
onSuccess: (tx: string) => Promise<void>;
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
index e92eb78c0..f4b63955d 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
@@ -41,7 +41,7 @@ describe("Payment CTA states", () => {
it("should tell the user that the URI is missing", async () => {
const { handler, TestingContext } = createWalletApiMock();
const props = {
- talerPayUri: undefined,
+ talerPayUri: "",
cancel: nullFunction,
goToWalletManualWithdraw: nullFunction,
onSuccess: nullFunction,
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
index 2cdc8d2e1..f5a8c8814 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
@@ -20,12 +20,25 @@ import { ErrorAlert } from "../../context/alert.js";
import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
+import { PaymentPage } from "../Payment/index.js";
+import {
+ AmountFieldHandler,
+ ButtonHandler,
+ TextFieldHandler,
+} from "../../mui/handlers.js";
export interface Props {
- talerTemplateUri?: string;
+ talerTemplateUri: string;
+ goToWalletManualWithdraw: (amount?: string) => Promise<void>;
+ cancel: () => Promise<void>;
+ onSuccess: (tx: string) => Promise<void>;
}
-export type State = State.Loading | State.LoadingUriError | State.Ready;
+export type State =
+ | State.Loading
+ | State.LoadingUriError
+ | State.OrderReady
+ | State.FillTemplate;
export namespace State {
export interface Loading {
@@ -37,16 +50,30 @@ export namespace State {
error: ErrorAlert;
}
- export interface Ready {
- status: "ready";
+ export interface FillTemplate {
+ status: "fill-template";
error: undefined;
+ currency: string;
+ amount?: AmountFieldHandler;
+ summary?: TextFieldHandler;
+ onCreate: ButtonHandler;
+ }
+
+ export interface OrderReady {
+ status: "order-ready";
+ error: undefined;
+ talerPayUri: string;
+ onSuccess: (tx: string) => Promise<void>;
+ cancel: () => Promise<void>;
+ goToWalletManualWithdraw: () => Promise<void>;
}
}
const viewMapping: StateViewMap<State> = {
loading: Loading,
error: ErrorAlertView,
- ready: ReadyView,
+ "fill-template": ReadyView,
+ "order-ready": PaymentPage,
};
export const PaymentTemplatePage = compose(
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
index f5e6dee61..abcf040b7 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
@@ -14,27 +14,56 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { Amounts } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { alertFromError } from "../../context/alert.js";
+import { useState } from "preact/hooks";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "../../context/translation.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
+import { AmountFieldHandler, TextFieldHandler } from "../../mui/handlers.js";
import { Props, State } from "./index.js";
-export function useComponentState({ talerTemplateUri }: Props): State {
- // const { pushAlertOnError } = useAlertContext();
+export function useComponentState({
+ talerTemplateUri,
+ cancel,
+ goToWalletManualWithdraw,
+ onSuccess,
+}: Props): State {
const api = useBackendContext();
const { i18n } = useTranslationContext();
+ const { safely } = useAlertContext();
+
+ const url = talerTemplateUri ? new URL(talerTemplateUri) : undefined;
+
+ const amountParam = !url
+ ? undefined
+ : url.searchParams.get("amount") ?? undefined;
+ const summaryParam = !url
+ ? undefined
+ : url.searchParams.get("summary") ?? undefined;
+
+ const parsedAmount = !amountParam ? undefined : Amounts.parse(amountParam);
+ const currency = parsedAmount ? parsedAmount.currency : amountParam;
+
+ const initialAmount =
+ parsedAmount ?? (currency ? Amounts.zeroOfCurrency(currency) : undefined);
+ const [amount, setAmount] = useState(initialAmount);
+ const [summary, setSummary] = useState(summaryParam);
+ const [newOrder, setNewOrder] = useState("");
const hook = useAsyncAsHook(async () => {
if (!talerTemplateUri) throw Error("ERROR_NO-URI-FOR-PAYMENT-TEMPLATE");
- const payStatus = await api.wallet.call(
- WalletApiOperation.PreparePayForTemplate,
- {
- talerPayTemplateUri: talerTemplateUri,
- templateParams: {},
- },
- );
+ let payStatus;
+ if (!amountParam && !summaryParam) {
+ payStatus = await api.wallet.call(
+ WalletApiOperation.PreparePayForTemplate,
+ {
+ talerPayTemplateUri: talerTemplateUri,
+ templateParams: {},
+ },
+ );
+ }
const balance = await api.wallet.call(WalletApiOperation.GetBalances, {});
return { payStatus, balance, uri: talerTemplateUri };
}, []);
@@ -56,8 +85,85 @@ export function useComponentState({ talerTemplateUri }: Props): State {
};
}
+ if (hook.response.payStatus) {
+ return {
+ status: "order-ready",
+ error: undefined,
+ cancel,
+ goToWalletManualWithdraw,
+ onSuccess,
+ talerPayUri: hook.response.payStatus.talerUri!,
+ };
+ }
+
+ if (newOrder) {
+ return {
+ status: "order-ready",
+ error: undefined,
+ cancel,
+ goToWalletManualWithdraw,
+ onSuccess,
+ talerPayUri: newOrder,
+ };
+ }
+
+ async function createOrder() {
+ try {
+ const templateParams: Record<string, string> = {};
+ if (amount) {
+ templateParams["amount"] = Amounts.stringify(amount);
+ }
+ if (summary) {
+ templateParams["summary"] = summary;
+ }
+ const payStatus = await api.wallet.call(
+ WalletApiOperation.PreparePayForTemplate,
+ {
+ talerPayTemplateUri: talerTemplateUri,
+ templateParams,
+ },
+ );
+ setNewOrder(payStatus.talerUri!);
+ } catch (e) {}
+ }
+ const errors = undefinedIfEmpty({
+ amount: amount && Amounts.isZero(amount) ? i18n.str`required` : undefined,
+ summary: !summary ? i18n.str`required` : undefined,
+ });
return {
- status: "ready",
+ status: "fill-template",
error: undefined,
+ currency: currency!, //currency is always not null
+ amount:
+ amount !== undefined
+ ? ({
+ onInput: (a) => {
+ setAmount(a);
+ },
+ value: amount,
+ error: errors?.amount,
+ } as AmountFieldHandler)
+ : undefined,
+ summary:
+ summary !== undefined
+ ? ({
+ onInput: (t) => {
+ setSummary(t);
+ },
+ value: summary,
+ error: errors?.summary,
+ } as TextFieldHandler)
+ : undefined,
+ onCreate: {
+ onClick: errors
+ ? undefined
+ : safely(createOrder, i18n.str`Could not create order`),
+ },
};
}
+
+function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
+ return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+ ? obj
+ : undefined;
+}
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
index 32a080959..93421eaa3 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
@@ -29,6 +29,6 @@ export default {
};
export const PaymentPossible = tests.createExample(ReadyView, {
- status: "ready",
+ status: "fill-template",
error: undefined,
});
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
index d4c65e008..72fbb6853 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
@@ -21,6 +21,7 @@
import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js";
+import { nullFunction } from "../../mui/handlers.js";
import { createWalletApiMock } from "../../test-utils.js";
import { useComponentState } from "./state.js";
@@ -28,7 +29,10 @@ describe("Order template CTA states", () => {
it("should tell the user that the URI is missing", async () => {
const { handler, TestingContext } = createWalletApiMock();
const props = {
- talerTemplateUri: undefined,
+ talerTemplateUri: "",
+ cancel: nullFunction,
+ goToWalletManualWithdraw: nullFunction,
+ onSuccess: nullFunction,
};
const hookBehavior = await tests.hookBehaveLikeThis(
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
index d3f893c7e..9f4c0f28c 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
@@ -15,15 +15,64 @@
*/
import { Fragment, h, VNode } from "preact";
+import { AmountField } from "../../components/AmountField.js";
+import { Part } from "../../components/Part.js";
import { useTranslationContext } from "../../context/translation.js";
+import { Button } from "../../mui/Button.js";
+import { TextField } from "../../mui/TextField.js";
import { State } from "./index.js";
-export function ReadyView({ status }: State.Ready): VNode {
+export function ReadyView({
+ currency,
+ amount,
+ summary,
+ onCreate,
+}: State.FillTemplate): VNode {
const { i18n } = useTranslationContext();
+ console.log("is summary", !!summary);
return (
- <div>
- <i18n.Translate>Not yet implemented</i18n.Translate>
- </div>
+ <Fragment>
+ <section style={{ textAlign: "left" }}>
+ {/* <Part
+ title={
+ <div
+ style={{
+ display: "flex",
+ alignItems: "center",
+ }}
+ >
+ <i18n.Translate>Merchant</i18n.Translate>
+ </div>
+ }
+ text={<ExchangeDetails exchange={exchangeUrl} />}
+ kind="neutral"
+ big
+ /> */}
+ {!amount ? undefined : (
+ <p>
+ <AmountField label={i18n.str`Amount`} handler={amount} />
+ </p>
+ )}
+ {!summary ? undefined : (
+ <p>
+ <TextField
+ label="Summary"
+ variant="filled"
+ required
+ fullWidth
+ error={summary.error}
+ value={summary.value}
+ onChange={summary.onInput}
+ />
+ </p>
+ )}
+ </section>
+ <section>
+ <Button onClick={onCreate.onClick} variant="contained" color="success">
+ <i18n.Translate>Review order</i18n.Translate>
+ </Button>
+ </section>
+ </Fragment>
);
}