aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx33
-rw-r--r--packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx68
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.tsx57
-rw-r--r--packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx48
-rw-r--r--packages/taler-wallet-webextension/src/utils/index.ts51
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.stories.tsx5
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.tsx60
7 files changed, 235 insertions, 87 deletions
diff --git a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx b/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
new file mode 100644
index 000000000..6ee56ef77
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
@@ -0,0 +1,33 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createExample } from "../test-utils";
+import { AddNewActionView as TestedComponent } from "./AddNewActionView";
+
+export default {
+ title: "popup/add new action",
+ component: TestedComponent,
+ argTypes: {
+ setDeviceName: () => Promise.resolve(),
+ },
+};
+
+export const Initial = createExample(TestedComponent, {});
diff --git a/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx b/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx
new file mode 100644
index 000000000..876b1a83c
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx
@@ -0,0 +1,68 @@
+import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import {
+ Button,
+ ButtonSuccess,
+ InputWithLabel,
+} from "../components/styled/index";
+import { actionForTalerUri } from "../utils/index";
+
+export interface Props {
+ onCancel: () => void;
+}
+
+function buttonLabelByTalerType(type: TalerUriType): string {
+ switch (type) {
+ case TalerUriType.TalerNotifyReserve:
+ return "Open reserve page";
+ case TalerUriType.TalerPay:
+ return "Open pay page";
+ case TalerUriType.TalerRefund:
+ return "Open refund page";
+ case TalerUriType.TalerTip:
+ return "Open tip page";
+ case TalerUriType.TalerWithdraw:
+ return "Open withdraw page";
+ }
+ return "";
+}
+
+export function AddNewActionView({ onCancel }: Props): VNode {
+ const [url, setUrl] = useState("");
+ const uriType = classifyTalerUri(url);
+
+ return (
+ <Fragment>
+ <section>
+ <InputWithLabel
+ invalid={url !== "" && uriType === TalerUriType.Unknown}
+ >
+ <label>GNU Taler URI</label>
+ <div>
+ <input
+ style={{ width: "100%" }}
+ type="text"
+ value={url}
+ placeholder="taler://pay/...."
+ onInput={(e) => setUrl(e.currentTarget.value)}
+ />
+ </div>
+ </InputWithLabel>
+ </section>
+ <footer>
+ <Button onClick={onCancel}>Back</Button>
+ {uriType !== TalerUriType.Unknown && (
+ <ButtonSuccess
+ onClick={() => {
+ // eslint-disable-next-line no-undef
+ chrome.tabs.create({ url: actionForTalerUri(uriType, url) });
+ }}
+ >
+ {buttonLabelByTalerType(uriType)}
+ </ButtonSuccess>
+ )}
+ </footer>
+ </Fragment>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx
index b23b4781f..f897299d8 100644
--- a/packages/taler-wallet-webextension/src/popup/History.tsx
+++ b/packages/taler-wallet-webextension/src/popup/History.tsx
@@ -23,10 +23,11 @@ import {
} from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
-import { PopupBox } from "../components/styled";
+import { ButtonPrimary } from "../components/styled/index";
import { TransactionItem } from "../components/TransactionItem";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import * as wxApi from "../wxApi";
+import { AddNewActionView } from "./AddNewActionView";
export function HistoryPage(): VNode {
const [transactions, setTransactions] = useState<
@@ -45,6 +46,12 @@ export function HistoryPage(): VNode {
fetchData();
}, []);
+ const [addingAction, setAddingAction] = useState(false);
+
+ if (addingAction) {
+ return <AddNewActionView onCancel={() => setAddingAction(false)} />;
+ }
+
if (!transactions) {
return <div>Loading ...</div>;
}
@@ -53,6 +60,7 @@ export function HistoryPage(): VNode {
<HistoryView
balances={balanceWithoutError}
list={[...transactions.transactions].reverse()}
+ onAddNewAction={() => setAddingAction(true)}
/>
);
}
@@ -65,31 +73,42 @@ function amountToString(c: AmountString): string {
export function HistoryView({
list,
balances,
+ onAddNewAction,
}: {
list: Transaction[];
balances: Balance[];
+ onAddNewAction: () => void;
}): VNode {
const multiCurrency = balances.length > 1;
return (
<Fragment>
- {balances.length > 0 && (
- <header>
- {multiCurrency ? (
- <div class="title">
- Balance:{" "}
- <ul style={{ margin: 0 }}>
- {balances.map((b, i) => (
- <li key={i}>{b.available}</li>
- ))}
- </ul>
- </div>
- ) : (
- <div class="title">
- Balance: <span>{amountToString(balances[0].available)}</span>
- </div>
- )}
- </header>
- )}
+ <header>
+ {balances.length > 0 ? (
+ <Fragment>
+ {multiCurrency ? (
+ <div class="title">
+ Balance:{" "}
+ <ul style={{ margin: 0 }}>
+ {balances.map((b, i) => (
+ <li key={i}>{b.available}</li>
+ ))}
+ </ul>
+ </div>
+ ) : (
+ <div class="title">
+ Balance: <span>{amountToString(balances[0].available)}</span>
+ </div>
+ )}
+ </Fragment>
+ ) : (
+ <div />
+ )}
+ <div>
+ <ButtonPrimary onClick={onAddNewAction}>
+ <b>+</b>
+ </ButtonPrimary>
+ </div>
+ </header>
{list.length === 0 ? (
<section data-expanded data-centered>
<p>
diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
index b2220e37b..40e9111fb 100644
--- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
+++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
@@ -22,6 +22,7 @@
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
import { Fragment, h } from "preact";
import { ButtonPrimary, ButtonSuccess } from "../components/styled/index";
+import { actionForTalerUri } from "../utils/index";
export interface Props {
url: string;
@@ -108,50 +109,3 @@ export function TalerActionFound({ url, onDismiss }: Props) {
</Fragment>
);
}
-
-function actionForTalerUri(
- uriType: TalerUriType,
- talerUri: string,
-): string | undefined {
- switch (uriType) {
- case TalerUriType.TalerWithdraw:
- return makeExtensionUrlWithParams("static/wallet.html#/withdraw", {
- talerWithdrawUri: talerUri,
- });
- case TalerUriType.TalerPay:
- return makeExtensionUrlWithParams("static/wallet.html#/pay", {
- talerPayUri: talerUri,
- });
- case TalerUriType.TalerTip:
- return makeExtensionUrlWithParams("static/wallet.html#/tip", {
- talerTipUri: talerUri,
- });
- case TalerUriType.TalerRefund:
- return makeExtensionUrlWithParams("static/wallet.html#/refund", {
- talerRefundUri: talerUri,
- });
- case TalerUriType.TalerNotifyReserve:
- // FIXME: implement
- break;
- default:
- console.warn(
- "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
- );
- break;
- }
- return undefined;
-}
-
-function makeExtensionUrlWithParams(
- url: string,
- params?: { [name: string]: string | undefined },
-): string {
- const innerUrl = new URL(chrome.extension.getURL("/" + url));
- if (params) {
- const hParams = Object.keys(params)
- .map((k) => `${k}=${params[k]}`)
- .join("&");
- innerUrl.hash = innerUrl.hash + "?" + hParams;
- }
- return innerUrl.href;
-}
diff --git a/packages/taler-wallet-webextension/src/utils/index.ts b/packages/taler-wallet-webextension/src/utils/index.ts
index 15081f920..8eb89d58f 100644
--- a/packages/taler-wallet-webextension/src/utils/index.ts
+++ b/packages/taler-wallet-webextension/src/utils/index.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson, Amounts, GetExchangeTosResult } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, GetExchangeTosResult, TalerUriType } from "@gnu-taler/taler-util";
function getJsonIfOk(r: Response): Promise<any> {
@@ -164,3 +164,52 @@ export function amountToString(text: AmountJson): string {
return `${amount} ${aj.currency}`;
}
+export function actionForTalerUri(
+ uriType: TalerUriType,
+ talerUri: string,
+): string | undefined {
+ switch (uriType) {
+ case TalerUriType.TalerWithdraw:
+ return makeExtensionUrlWithParams("static/wallet.html#/withdraw", {
+ talerWithdrawUri: talerUri,
+ });
+ case TalerUriType.TalerPay:
+ return makeExtensionUrlWithParams("static/wallet.html#/pay", {
+ talerPayUri: talerUri,
+ });
+ case TalerUriType.TalerTip:
+ return makeExtensionUrlWithParams("static/wallet.html#/tip", {
+ talerTipUri: talerUri,
+ });
+ case TalerUriType.TalerRefund:
+ return makeExtensionUrlWithParams("static/wallet.html#/refund", {
+ talerRefundUri: talerUri,
+ });
+ case TalerUriType.TalerNotifyReserve:
+ // FIXME: implement
+ break;
+ default:
+ console.warn(
+ "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
+ );
+ break;
+ }
+ return undefined;
+}
+
+function makeExtensionUrlWithParams(
+ url: string,
+ params?: { [name: string]: string | undefined },
+): string {
+ // eslint-disable-next-line no-undef
+ const innerUrl = new URL(chrome.extension.getURL("/" + url));
+ if (params) {
+ const hParams = Object.keys(params)
+ .map((k) => `${k}=${params[k]}`)
+ .join("&");
+ innerUrl.hash = innerUrl.hash + "?" + hParams;
+ }
+ return innerUrl.href;
+}
+
+
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index 0f471ac30..ce4b0fb74 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -127,6 +127,11 @@ export const Empty = createExample(TestedComponent, {
],
});
+export const EmptyWithNoBalance = createExample(TestedComponent, {
+ list: [],
+ balances: [],
+});
+
export const One = createExample(TestedComponent, {
list: [exampleData.withdraw],
balances: [
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx
index bc8ef734a..58db0360b 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -21,10 +21,12 @@ import {
Transaction,
} from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
-import { DateSeparator } from "../components/styled";
+import { useState } from "preact/hooks";
+import { ButtonPrimary, DateSeparator } from "../components/styled";
import { Time } from "../components/Time";
import { TransactionItem } from "../components/TransactionItem";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { AddNewActionView } from "../popup/AddNewActionView";
import * as wxApi from "../wxApi";
export function HistoryPage(): VNode {
@@ -37,6 +39,12 @@ export function HistoryPage(): VNode {
NotificationType.WithdrawGroupFinished,
]);
+ const [addingAction, setAddingAction] = useState(false);
+
+ if (addingAction) {
+ return <AddNewActionView onCancel={() => setAddingAction(false)} />;
+ }
+
if (!transactionQuery) {
return <div>Loading ...</div>;
}
@@ -48,6 +56,7 @@ export function HistoryPage(): VNode {
<HistoryView
balances={balanceWithoutError}
list={[...transactionQuery.response.transactions].reverse()}
+ onAddNewAction={() => setAddingAction(true)}
/>
);
}
@@ -65,9 +74,11 @@ function normalizeToDay(x: number): number {
export function HistoryView({
list,
balances,
+ onAddNewAction,
}: {
list: Transaction[];
balances: Balance[];
+ onAddNewAction: () => void;
}): VNode {
const byDate = list.reduce((rv, x) => {
const theDate =
@@ -83,25 +94,34 @@ export function HistoryView({
return (
<Fragment>
- {balances.length > 0 && (
- <header>
- {balances.length === 1 && (
- <div class="title">
- Balance: <span>{amountToString(balances[0].available)}</span>
- </div>
- )}
- {balances.length > 1 && (
- <div class="title">
- Balance:{" "}
- <ul style={{ margin: 0 }}>
- {balances.map((b, i) => (
- <li key={i}>{b.available}</li>
- ))}
- </ul>
- </div>
- )}
- </header>
- )}
+ <header>
+ {balances.length > 0 ? (
+ <Fragment>
+ {balances.length === 1 && (
+ <div class="title">
+ Balance: <span>{amountToString(balances[0].available)}</span>
+ </div>
+ )}
+ {balances.length > 1 && (
+ <div class="title">
+ Balance:{" "}
+ <ul style={{ margin: 0 }}>
+ {balances.map((b, i) => (
+ <li key={i}>{b.available}</li>
+ ))}
+ </ul>
+ </div>
+ )}
+ </Fragment>
+ ) : (
+ <div />
+ )}
+ <div>
+ <ButtonPrimary onClick={onAddNewAction}>
+ <b>+</b>
+ </ButtonPrimary>
+ </div>
+ </header>
<section>
{Object.keys(byDate).map((d, i) => {
return (