summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-06-16 18:21:03 -0300
committerSebastian <sebasjm@gmail.com>2021-06-16 18:21:15 -0300
commit0b4976601fe2ecb0462fe72ae188b5cbba06d9cc (patch)
tree93f18c88d754430d2c1261109c1e9b6c1ac7a500 /packages/taler-wallet-webextension
parentd58945c830a33910dd93bc159c1ffe5d490df846 (diff)
downloadwallet-core-0b4976601fe2ecb0462fe72ae188b5cbba06d9cc.tar.gz
wallet-core-0b4976601fe2ecb0462fe72ae188b5cbba06d9cc.tar.bz2
wallet-core-0b4976601fe2ecb0462fe72ae188b5cbba06d9cc.zip
components renaming to follow react pattern
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx47
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts53
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.tsx24
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts93
-rw-r--r--packages/taler-wallet-webextension/src/popup/Balance.tsx173
-rw-r--r--packages/taler-wallet-webextension/src/popup/Debug.tsx63
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.tsx227
-rw-r--r--packages/taler-wallet-webextension/src/popup/Settings.tsx34
-rw-r--r--packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx (renamed from packages/taler-wallet-webextension/src/popup/popup.stories.tsx)37
-rw-r--r--packages/taler-wallet-webextension/src/popup/Transaction.tsx327
-rw-r--r--packages/taler-wallet-webextension/src/popup/popup.tsx899
-rw-r--r--packages/taler-wallet-webextension/src/popupEntryPoint.tsx43
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Pay.tsx (renamed from packages/taler-wallet-webextension/src/wallet/pay.tsx)13
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Refund.tsx (renamed from packages/taler-wallet-webextension/src/wallet/refund.tsx)21
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Tip.tsx (renamed from packages/taler-wallet-webextension/src/wallet/tip.tsx)14
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.tsx (renamed from packages/taler-wallet-webextension/src/wallet/welcome.tsx)42
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx (renamed from packages/taler-wallet-webextension/src/wallet/withdraw.stories.tsx)2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Withdraw.tsx (renamed from packages/taler-wallet-webextension/src/wallet/withdraw.tsx)14
-rw-r--r--packages/taler-wallet-webextension/src/walletEntryPoint.tsx20
19 files changed, 1068 insertions, 1078 deletions
diff --git a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx
new file mode 100644
index 000000000..7534629fb
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+ import { JSX } from "preact";
+
+export function DebugCheckbox({ enabled, onToggle }: { enabled: boolean; onToggle: () => void; }): JSX.Element {
+ return (
+ <div>
+ <input
+ checked={enabled}
+ onClick={onToggle}
+ type="checkbox"
+ id="checkbox-perm"
+ style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} />
+ <label
+ htmlFor="checkbox-perm"
+ style={{ marginLeft: "0.5em", fontWeight: "bold" }}
+ >
+ Automatically open wallet based on page content
+ </label>
+ <span
+ style={{
+ color: "#383838",
+ fontSize: "smaller",
+ display: "block",
+ marginLeft: "2em",
+ }}
+ >
+ (Enabling this option below will make using the wallet faster, but
+ requires more permissions from your browser.)
+ </span>
+ </div>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts
new file mode 100644
index 000000000..809863dc5
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts
@@ -0,0 +1,53 @@
+import { useState, useEffect } from "preact/hooks";
+import * as wxApi from "../wxApi";
+import { getPermissionsApi } from "../compat";
+import { extendedPermissions } from "../permissions";
+
+
+export function useExtendedPermissions(): [boolean, () => void] {
+ const [enabled, setEnabled] = useState(false);
+
+ const toggle = () => {
+ setEnabled(v => !v);
+ handleExtendedPerm(enabled).then(result => {
+ setEnabled(result);
+ });
+ };
+
+ useEffect(() => {
+ async function getExtendedPermValue(): Promise<void> {
+ const res = await wxApi.getExtendedPermissions();
+ setEnabled(res.newValue);
+ }
+ getExtendedPermValue();
+ }, []);
+ return [enabled, toggle];
+}
+
+async function handleExtendedPerm(isEnabled: boolean): Promise<boolean> {
+ let nextVal: boolean | undefined;
+
+ if (!isEnabled) {
+ const granted = await new Promise<boolean>((resolve, reject) => {
+ // We set permissions here, since apparently FF wants this to be done
+ // as the result of an input event ...
+ getPermissionsApi().request(extendedPermissions, (granted: boolean) => {
+ if (chrome.runtime.lastError) {
+ console.error("error requesting permissions");
+ console.error(chrome.runtime.lastError);
+ reject(chrome.runtime.lastError);
+ return;
+ }
+ console.log("permissions granted:", granted);
+ resolve(granted);
+ });
+ });
+ const res = await wxApi.setExtendedPermissions(granted);
+ nextVal = res.newValue;
+ } else {
+ const res = await wxApi.setExtendedPermissions(false);
+ nextVal = res.newValue;
+ }
+ console.log("new permissions applied:", nextVal ?? false);
+ return nextVal ?? false
+} \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.tsx b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.tsx
deleted file mode 100644
index f5c788cf6..000000000
--- a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useState, useEffect } from "preact/hooks";
-import * as wxApi from "../wxApi";
-import { handleExtendedPerm } from "../wallet/welcome";
-
-
-export function useExtendedPermissions(): [boolean, () => void] {
- const [enabled, setEnabled] = useState(false);
-
- const toggle = () => {
- setEnabled(v => !v);
- handleExtendedPerm(enabled).then(result => {
- setEnabled(result);
- });
- };
-
- useEffect(() => {
- async function getExtendedPermValue(): Promise<void> {
- const res = await wxApi.getExtendedPermissions();
- setEnabled(res.newValue);
- }
- getExtendedPermValue();
- }, []);
- return [enabled, toggle];
-}
diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
new file mode 100644
index 000000000..b884ca943
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
@@ -0,0 +1,93 @@
+import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
+import { useEffect, useState } from "preact/hooks";
+
+export function useTalerActionURL(): [string | undefined, (s: boolean) => void] {
+ const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
+ undefined
+ );
+ const [dismissed, setDismissed] = useState(false);
+ useEffect(() => {
+ async function check(): Promise<void> {
+ const talerUri = await findTalerUriInActiveTab();
+ if (talerUri) {
+ const actionUrl = actionForTalerUri(talerUri);
+ setTalerActionUrl(actionUrl);
+ }
+ }
+ check();
+ }, []);
+ const url = dismissed ? undefined : talerActionUrl;
+ return [url, setDismissed];
+}
+
+function actionForTalerUri(talerUri: string): string | undefined {
+ const uriType = classifyTalerUri(talerUri);
+ 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) {
+ for (const key in params) {
+ const p = params[key];
+ if (p) {
+ innerUrl.searchParams.set(key, p);
+ }
+ }
+ }
+ return innerUrl.href;
+}
+
+async function findTalerUriInActiveTab(): Promise<string | undefined> {
+ return new Promise((resolve, reject) => {
+ chrome.tabs.executeScript(
+ {
+ code: `
+ (() => {
+ let x = document.querySelector("a[href^='taler://'") || document.querySelector("a[href^='taler+http://'");
+ return x ? x.href.toString() : null;
+ })();
+ `,
+ allFrames: false,
+ },
+ (result) => {
+ if (chrome.runtime.lastError) {
+ console.error(chrome.runtime.lastError);
+ resolve(undefined);
+ return;
+ }
+ console.log("got result", result);
+ resolve(result[0]);
+ },
+ );
+ });
+}
diff --git a/packages/taler-wallet-webextension/src/popup/Balance.tsx b/packages/taler-wallet-webextension/src/popup/Balance.tsx
new file mode 100644
index 000000000..77d2c4201
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/Balance.tsx
@@ -0,0 +1,173 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+ Amounts,
+ BalancesResponse,
+ Balance, i18n, AmountJson, amountFractionalBase
+} from "@gnu-taler/taler-util";
+import { Component, JSX } from "preact";
+import { PageLink, renderAmount } from "../renderHtml";
+import * as wxApi from "../wxApi";
+
+
+/**
+ * Render an amount as a large number with a small currency symbol.
+ */
+function bigAmount(amount: AmountJson): JSX.Element {
+ const v = amount.value + amount.fraction / amountFractionalBase;
+ return (
+ <span>
+ <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "}
+ <span>{amount.currency}</span>
+ </span>
+ );
+}
+
+function EmptyBalanceView(): JSX.Element {
+ return (
+ <p><i18n.Translate>
+ You have no balance to show. Need some{" "}
+ <PageLink pageName="/welcome">help</PageLink> getting started?
+ </i18n.Translate></p>
+ );
+}
+
+
+export class BalancePage extends Component<any, any> {
+ private balance?: BalancesResponse;
+ private gotError = false;
+ private canceler: (() => void) | undefined = undefined;
+ private unmount = false;
+ private updateBalanceRunning = false;
+
+ componentWillMount(): void {
+ this.canceler = wxApi.onUpdateNotification(() => this.updateBalance());
+ this.updateBalance();
+ }
+
+ componentWillUnmount(): void {
+ console.log("component WalletBalanceView will unmount");
+ if (this.canceler) {
+ this.canceler();
+ }
+ this.unmount = true;
+ }
+
+ async updateBalance(): Promise<void> {
+ if (this.updateBalanceRunning) {
+ return;
+ }
+ this.updateBalanceRunning = true;
+ let balance: BalancesResponse;
+ try {
+ balance = await wxApi.getBalance();
+ } catch (e) {
+ if (this.unmount) {
+ return;
+ }
+ this.gotError = true;
+ console.error("could not retrieve balances", e);
+ this.setState({});
+ return;
+ } finally {
+ this.updateBalanceRunning = false;
+ }
+ if (this.unmount) {
+ return;
+ }
+ this.gotError = false;
+ console.log("got balance", balance);
+ this.balance = balance;
+ this.setState({});
+ }
+
+ formatPending(entry: Balance): JSX.Element {
+ let incoming: JSX.Element | undefined;
+ let payment: JSX.Element | undefined;
+
+ const available = Amounts.parseOrThrow(entry.available);
+ const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming);
+ const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
+
+ console.log(
+ "available: ",
+ entry.pendingIncoming ? renderAmount(entry.available) : null
+ );
+ console.log(
+ "incoming: ",
+ entry.pendingIncoming ? renderAmount(entry.pendingIncoming) : null
+ );
+
+ if (!Amounts.isZero(pendingIncoming)) {
+ incoming = (
+ <span><i18n.Translate>
+ <span style={{ color: "darkgreen" }}>
+ {"+"}
+ {renderAmount(entry.pendingIncoming)}
+ </span>{" "}
+ incoming
+ </i18n.Translate></span>
+ );
+ }
+
+ const l = [incoming, payment].filter((x) => x !== undefined);
+ if (l.length === 0) {
+ return <span />;
+ }
+
+ if (l.length === 1) {
+ return <span>({l})</span>;
+ }
+ return (
+ <span>
+ ({l[0]}, {l[1]})
+ </span>
+ );
+ }
+
+ render(): JSX.Element {
+ const wallet = this.balance;
+ if (this.gotError) {
+ return (
+ <div className="balance">
+ <p>{i18n.str`Error: could not retrieve balance information.`}</p>
+ <p>
+ Click <PageLink pageName="welcome.html">here</PageLink> for help and
+ diagnostics.
+ </p>
+ </div>
+ );
+ }
+ if (!wallet) {
+ return <span></span>;
+ }
+
+ const listing = wallet.balances.map((entry) => {
+ const av = Amounts.parseOrThrow(entry.available);
+ return (
+ <p key={av.currency}>
+ {bigAmount(av)} {this.formatPending(entry)}
+ </p>
+ );
+ });
+ return listing.length > 0 ? (
+ <div className="balance">{listing}</div>
+ ) : (
+ <EmptyBalanceView />
+ );
+ }
+}
diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx b/packages/taler-wallet-webextension/src/popup/Debug.tsx
new file mode 100644
index 000000000..073dac2ca
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx
@@ -0,0 +1,63 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { JSX } from "preact";
+import { Diagnostics } from "../components/Diagnostics";
+import * as wxApi from "../wxApi";
+
+
+export function DebugPage(props: any): JSX.Element {
+ return (
+ <div>
+ <p>Debug tools:</p>
+ <button onClick={openExtensionPage("/static/popup.html")}>wallet tab</button>
+ <br />
+ <button onClick={confirmReset}>reset</button>
+ <button onClick={reload}>reload chrome extension</button>
+ <Diagnostics />
+ </div>
+ );
+}
+
+export function reload(): void {
+ try {
+ chrome.runtime.reload();
+ window.close();
+ } catch (e) {
+ // Functionality missing in firefox, ignore!
+ }
+}
+
+export async function confirmReset(): Promise<void> {
+ if (
+ confirm(
+ "Do you want to IRREVOCABLY DESTROY everything inside your" +
+ " wallet and LOSE ALL YOUR COINS?",
+ )
+ ) {
+ await wxApi.resetDb();
+ window.close();
+ }
+}
+
+export function openExtensionPage(page: string) {
+ return () => {
+ chrome.tabs.create({
+ url: chrome.extension.getURL(page),
+ });
+ };
+}
+
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx
new file mode 100644
index 000000000..ffcec5e41
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/History.tsx
@@ -0,0 +1,227 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { AmountString, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
+import { JSX } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import * as wxApi from "../wxApi";
+import { Pages } from "./popup";
+
+
+export function HistoryPage(props: any): JSX.Element {
+ const [transactions, setTransactions] = useState<
+ TransactionsResponse | undefined
+ >(undefined);
+
+ useEffect(() => {
+ const fetchData = async (): Promise<void> => {
+ const res = await wxApi.getTransactions();
+ setTransactions(res);
+ };
+ fetchData();
+ }, []);
+
+ if (!transactions) {
+ return <div>Loading ...</div>;
+ }
+
+ const txs = [...transactions.transactions].reverse();
+
+ return (
+ <div>
+ {txs.map((tx, i) => (
+ <TransactionItem key={i} tx={tx} />
+ ))}
+ </div>
+ );
+}
+
+function TransactionItem(props: { tx: Transaction }): JSX.Element {
+ const tx = props.tx;
+ switch (tx.type) {
+ case TransactionType.Withdrawal:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"credit"}
+ title="Withdrawal"
+ subtitle={`via ${tx.exchangeBaseUrl}`}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-bank-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ case TransactionType.Payment:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"debit"}
+ title="Payment"
+ subtitle={tx.info.summary}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-shopping-cart-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ case TransactionType.Refund:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"credit"}
+ title="Refund"
+ subtitle={tx.info.summary}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-refund-2-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ case TransactionType.Tip:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"credit"}
+ title="Tip"
+ subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-hand-heart-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ case TransactionType.Refresh:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"credit"}
+ title="Refresh"
+ subtitle={`via exchange ${tx.exchangeBaseUrl}`}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-refresh-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ case TransactionType.Deposit:
+ return (
+ <TransactionLayout
+ id={tx.transactionId}
+ amount={tx.amountEffective}
+ debitCreditIndicator={"debit"}
+ title="Refresh"
+ subtitle={`to ${tx.targetPaytoUri}`}
+ timestamp={tx.timestamp}
+ iconPath="/static/img/ri-refresh-line.svg"
+ pending={tx.pending}
+ ></TransactionLayout>
+ );
+ }
+}
+
+function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
+ const date = new Date(props.timestamp.t_ms);
+ const dateStr = date.toLocaleString([], {
+ dateStyle: "medium",
+ timeStyle: "short",
+ } as any);
+ return (
+ <div
+ style={{
+ display: "flex",
+ flexDirection: "row",
+ border: "1px solid gray",
+ borderRadius: "0.5em",
+ margin: "0.5em 0",
+ justifyContent: "space-between",
+ padding: "0.5em",
+ }}
+ >
+ <img src={props.iconPath} />
+ <div
+ style={{ display: "flex", flexDirection: "column", marginLeft: "1em" }}
+ >
+ <div style={{ fontSize: "small", color: "gray" }}>{dateStr}</div>
+ <div style={{ fontVariant: "small-caps", fontSize: "x-large" }}>
+ <a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a>
+ {props.pending ? (
+ <span style={{ color: "darkblue" }}> (Pending)</span>
+ ) : null}
+ </div>
+
+ <div>{props.subtitle}</div>
+ </div>
+ <TransactionAmount
+ pending={props.pending}
+ amount={props.amount}
+ debitCreditIndicator={props.debitCreditIndicator}
+ />
+ </div>
+ );
+}
+
+interface TransactionLayoutProps {
+ debitCreditIndicator: "debit" | "credit" | "unknown";
+ amount: AmountString | "unknown";
+ timestamp: Timestamp;
+ title: string;
+ id: string;
+ subtitle: string;
+ iconPath: string;
+ pending: boolean;
+}
+
+interface TransactionAmountProps {
+ debitCreditIndicator: "debit" | "credit" | "unknown";
+ amount: AmountString | "unknown";
+ pending: boolean;
+}
+
+function TransactionAmount(props: TransactionAmountProps): JSX.Element {
+ const [currency, amount] = props.amount.split(":");
+ let sign: string;
+ switch (props.debitCreditIndicator) {
+ case "credit":
+ sign = "+";
+ break;
+ case "debit":
+ sign = "-";
+ break;
+ case "unknown":
+ sign = "";
+ }
+ const style: JSX.AllCSSProperties = {
+ marginLeft: "auto",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ alignSelf: "center"
+ };
+ if (props.pending) {
+ style.color = "gray";
+ }
+ return (
+ <div style={{ ...style }}>
+ <div style={{ fontSize: "x-large" }}>
+ {sign}
+ {amount}
+ </div>
+ <div>{currency}</div>
+ </div>
+ );
+}
+
diff --git a/packages/taler-wallet-webextension/src/popup/Settings.tsx b/packages/taler-wallet-webextension/src/popup/Settings.tsx
new file mode 100644
index 000000000..5028b597c
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/Settings.tsx
@@ -0,0 +1,34 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+
+
+import { PermissionsCheckbox } from "../components/PermissionsCheckbox";
+import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
+
+
+export function SettingsPage() {
+ const [permissionsEnabled, togglePermissions] = useExtendedPermissions();
+ return (
+ <div>
+ <h2>Permissions</h2>
+ <PermissionsCheckbox enabled={permissionsEnabled} onToggle={togglePermissions} />
+ {/*
+ <h2>Developer mode</h2>
+ <DebugCheckbox enabled={permissionsEnabled} onToggle={togglePermissions} />
+ */}
+ </div>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/popup/popup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
index 0cb51a336..3df2687fd 100644
--- a/packages/taler-wallet-webextension/src/popup/popup.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
@@ -26,11 +26,12 @@ import {
TransactionWithdrawal,
WithdrawalType
} from '@gnu-taler/taler-util';
-import { WalletTransactionView as Component } from './popup';
+import { FunctionalComponent } from 'preact';
+import { TransactionView as TestedComponent } from './Transaction';
export default {
- title: 'popup/transaction details',
- component: Component,
+ title: 'popup/transaction/details',
+ component: TestedComponent,
decorators: [
(Story: any) => <div>
<link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
@@ -114,32 +115,32 @@ const exampleData = {
} as TransactionRefund,
}
-function dynamic<T>(props: any) {
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
const r = (args: any) => <Component {...args} />
r.args = props
return r
}
-export const NotYetLoaded = dynamic({});
+export const NotYetLoaded = createExample(TestedComponent,{});
-export const Withdraw = dynamic({
+export const Withdraw = createExample(TestedComponent,{
transaction: exampleData.withdraw
});
-export const WithdrawPending = dynamic({
+export const WithdrawPending = createExample(TestedComponent,{
transaction: { ...exampleData.withdraw, pending: true },
});
-export const Payment = dynamic({
+export const Payment = createExample(TestedComponent,{
transaction: exampleData.payment
});
-export const PaymentPending = dynamic({
+export const PaymentPending = createExample(TestedComponent,{
transaction: { ...exampleData.payment, pending: true },
});
-export const PaymentWithProducts = dynamic({
+export const PaymentWithProducts = createExample(TestedComponent,{
transaction: {
...exampleData.payment,
info: {
@@ -154,35 +155,35 @@ export const PaymentWithProducts = dynamic({
});
-export const Deposit = dynamic({
+export const Deposit = createExample(TestedComponent,{
transaction: exampleData.deposit
});
-export const DepositPending = dynamic({
+export const DepositPending = createExample(TestedComponent,{
transaction: { ...exampleData.deposit, pending: true }
});
-export const Refresh = dynamic({
+export const Refresh = createExample(TestedComponent,{
transaction: exampleData.refresh
});
-export const Tip = dynamic({
+export const Tip = createExample(TestedComponent,{
transaction: exampleData.tip
});
-export const TipPending = dynamic({
+export const TipPending = createExample(TestedComponent,{
transaction: { ...exampleData.tip, pending: true }
});
-export const Refund = dynamic({
+export const Refund = createExample(TestedComponent,{
transaction: exampleData.refund
});
-export const RefundPending = dynamic({
+export const RefundPending = createExample(TestedComponent,{
transaction: { ...exampleData.refund, pending: true }
});
-export const RefundWithProducts = dynamic({
+export const RefundWithProducts = createExample(TestedComponent,{
transaction: {
...exampleData.refund,
info: {
diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.tsx
new file mode 100644
index 000000000..b1179228e
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/Transaction.tsx
@@ -0,0 +1,327 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { Amounts, i18n, Transaction, TransactionType } from "@gnu-taler/taler-util";
+import { format } from "date-fns";
+import { JSX } from "preact";
+import { route } from 'preact-router';
+import { useEffect, useState } from "preact/hooks";
+import * as wxApi from "../wxApi";
+import { Pages } from "./popup";
+
+
+export function TransactionPage({ tid }: { tid: string; }): JSX.Element {
+ const [transaction, setTransaction] = useState<
+ Transaction | undefined
+ >(undefined);
+
+ useEffect(() => {
+ const fetchData = async (): Promise<void> => {
+ const res = await wxApi.getTransactions();
+ const ts = res.transactions.filter(t => t.transactionId === tid);
+ if (ts.length === 1) {
+ setTransaction(ts[0]);
+ } else {
+ route(Pages.history);
+ }
+ };
+ fetchData();
+ }, []);
+
+ return <TransactionView
+ transaction={transaction}
+ onDelete={() => wxApi.deleteTransaction(tid).then(_ => history.go(-1))}
+ onBack={() => { history.go(-1); }} />;
+}
+
+export interface WalletTransactionProps {
+ transaction?: Transaction,
+ onDelete: () => void,
+ onBack: () => void,
+}
+
+export function TransactionView({ transaction, onDelete, onBack }: WalletTransactionProps) {
+ if (!transaction) {
+ return <div><i18n.Translate>Loading ...</i18n.Translate></div>;
+ }
+
+ function Footer() {
+ return <footer style={{ marginTop: 'auto', display: 'flex' }}>
+ <button onClick={onBack}><i18n.Translate>back</i18n.Translate></button>
+ <div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}>
+ <button onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button>
+
+ </div>
+
+ </footer>
+ }
+
+ function Pending() {
+ if (!transaction?.pending) return null
+ return <span style={{ fontWeight: 'normal', fontSize: 16, color: 'gray' }}>(pending...)</span>
+ }
+
+ if (transaction.type === TransactionType.Withdrawal) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Withdrawal <Pending /></h1>
+ <p>
+ From <b>{transaction.exchangeBaseUrl}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Amount subtracted</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Amount received</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>Exchange fee</td>
+ <td>{Amounts.stringify(
+ Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount
+ )}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+ if (transaction.type === TransactionType.Payment) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Payment ({transaction.proposalId.substring(0, 10)}...) <Pending /></h1>
+ <p>
+ To <b>{transaction.info.merchant.name}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Order id</td>
+ <td>{transaction.info.orderId}</td>
+ </tr>
+ <tr>
+ <td>Summary</td>
+ <td>{transaction.info.summary}</td>
+ </tr>
+ {transaction.info.products && transaction.info.products.length > 0 &&
+ <tr>
+ <td>Products</td>
+ <td><ol style={{ margin: 0, textAlign: 'left' }}>
+ {transaction.info.products.map(p =>
+ <li>{p.description}</li>
+ )}</ol></td>
+ </tr>
+ }
+ <tr>
+ <td>Order amount</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Order amount and fees</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>Exchange fee</td>
+ <td>{Amounts.stringify(
+ Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountEffective),
+ Amounts.parseOrThrow(transaction.amountRaw),
+ ).amount
+ )}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+ if (transaction.type === TransactionType.Deposit) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Deposit ({transaction.depositGroupId}) <Pending /></h1>
+ <p>
+ To <b>{transaction.targetPaytoUri}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Amount deposit</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Amount deposit and fees</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>Exchange fee</td>
+ <td>{Amounts.stringify(
+ Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountEffective),
+ Amounts.parseOrThrow(transaction.amountRaw),
+ ).amount
+ )}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+ if (transaction.type === TransactionType.Refresh) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Refresh <Pending /></h1>
+ <p>
+ From <b>{transaction.exchangeBaseUrl}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Amount refreshed</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Fees</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+ if (transaction.type === TransactionType.Tip) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Tip <Pending /></h1>
+ <p>
+ From <b>{transaction.merchantBaseUrl}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Amount deduce</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Amount received</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>Exchange fee</td>
+ <td>{Amounts.stringify(
+ Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount
+ )}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+ const TRANSACTION_FROM_REFUND = /[a-z]*:([\w]{10}).*/
+ if (transaction.type === TransactionType.Refund) {
+ return (
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
+ <section>
+ <h1>Refund ({TRANSACTION_FROM_REFUND.exec(transaction.refundedTransactionId)![1]}...) <Pending /></h1>
+ <p>
+ From <b>{transaction.info.merchant.name}</b>
+ </p>
+ <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
+ <tr>
+ <td>Order id</td>
+ <td>{transaction.info.orderId}</td>
+ </tr>
+ <tr>
+ <td>Summary</td>
+ <td>{transaction.info.summary}</td>
+ </tr>
+ {transaction.info.products && transaction.info.products.length > 0 &&
+ <tr>
+ <td>Products</td>
+ <td><ol>
+ {transaction.info.products.map(p =>
+ <li>{p.description}</li>
+ )}</ol></td>
+ </tr>
+ }
+ <tr>
+ <td>Amount deduce</td>
+ <td>{transaction.amountRaw}</td>
+ </tr>
+ <tr>
+ <td>Amount received</td>
+ <td>{transaction.amountEffective}</td>
+ </tr>
+ <tr>
+ <td>Exchange fee</td>
+ <td>{Amounts.stringify(
+ Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount
+ )}</td>
+ </tr>
+ <tr>
+ <td>When</td>
+ <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
+ </tr>
+ </table>
+ </section>
+ <Footer />
+ </div>
+ );
+ }
+
+
+ return <div></div>
+}
diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/popup/popup.tsx
index 0f76d7728..95b87fad0 100644
--- a/packages/taler-wallet-webextension/src/popup/popup.tsx
+++ b/packages/taler-wallet-webextension/src/popup/popup.tsx
@@ -25,29 +25,9 @@
* Imports.
*/
import {
- AmountJson,
- Amounts,
- BalancesResponse,
- Balance,
- classifyTalerUri,
- TalerUriType,
- TransactionsResponse,
- Transaction,
- TransactionType,
- AmountString,
- Timestamp,
- amountFractionalBase,
- i18n,
+ classifyTalerUri, i18n, TalerUriType
} from "@gnu-taler/taler-util";
-import { format } from "date-fns";
-import { Component, ComponentChildren, Fragment, JSX } from "preact";
-import { route } from 'preact-router';
-import { useEffect, useState } from "preact/hooks";
-import { Diagnostics } from "../components/Diagnostics";
-import { PermissionsCheckbox } from "../components/PermissionsCheckbox";
-import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
-import { PageLink, renderAmount } from "../renderHtml";
-import * as wxApi from "../wxApi";
+import { ComponentChildren, JSX } from "preact";
export enum Pages {
balance = '/balance',
@@ -86,878 +66,3 @@ export function WalletNavBar({ current }: { current?: string }) {
);
}
-/**
- * Render an amount as a large number with a small currency symbol.
- */
-function bigAmount(amount: AmountJson): JSX.Element {
- const v = amount.value + amount.fraction / amountFractionalBase;
- return (
- <span>
- <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "}
- <span>{amount.currency}</span>
- </span>
- );
-}
-
-function EmptyBalanceView(): JSX.Element {
- return (
- <p><i18n.Translate>
- You have no balance to show. Need some{" "}
- <PageLink pageName="/welcome">help</PageLink> getting started?
- </i18n.Translate></p>
- );
-}
-
-export class WalletBalanceView extends Component<any, any> {
- private balance?: BalancesResponse;
- private gotError = false;
- private canceler: (() => void) | undefined = undefined;
- private unmount = false;
- private updateBalanceRunning = false;
-
- componentWillMount(): void {
- this.canceler = wxApi.onUpdateNotification(() => this.updateBalance());
- this.updateBalance();
- }
-
- componentWillUnmount(): void {
- console.log("component WalletBalanceView will unmount");
- if (this.canceler) {
- this.canceler();
- }
- this.unmount = true;
- }
-
- async updateBalance(): Promise<void> {
- if (this.updateBalanceRunning) {
- return;
- }
- this.updateBalanceRunning = true;
- let balance: BalancesResponse;
- try {
- balance = await wxApi.getBalance();
- } catch (e) {
- if (this.unmount) {
- return;
- }
- this.gotError = true;
- console.error("could not retrieve balances", e);
- this.setState({});
- return;
- } finally {
- this.updateBalanceRunning = false;
- }
- if (this.unmount) {
- return;
- }
- this.gotError = false;
- console.log("got balance", balance);
- this.balance = balance;
- this.setState({});
- }
-
- formatPending(entry: Balance): JSX.Element {
- let incoming: JSX.Element | undefined;
- let payment: JSX.Element | undefined;
-
- const available = Amounts.parseOrThrow(entry.available);
- const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming);
- const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
-
- console.log(
- "available: ",
- entry.pendingIncoming ? renderAmount(entry.available) : null,
- );
- console.log(
- "incoming: ",
- entry.pendingIncoming ? renderAmount(entry.pendingIncoming) : null,
- );
-
- if (!Amounts.isZero(pendingIncoming)) {
- incoming = (
- <span><i18n.Translate>
- <span style={{ color: "darkgreen" }}>
- {"+"}
- {renderAmount(entry.pendingIncoming)}
- </span>{" "}
- incoming
- </i18n.Translate></span>
- );
- }
-
- const l = [incoming, payment].filter((x) => x !== undefined);
- if (l.length === 0) {
- return <span />;
- }
-
- if (l.length === 1) {
- return <span>({l})</span>;
- }
- return (
- <span>
- ({l[0]}, {l[1]})
- </span>
- );
- }
-
- render(): JSX.Element {
- const wallet = this.balance;
- if (this.gotError) {
- return (
- <div className="balance">
- <p>{i18n.str`Error: could not retrieve balance information.`}</p>
- <p>
- Click <PageLink pageName="welcome.html">here</PageLink> for help and
- diagnostics.
- </p>
- </div>
- );
- }
- if (!wallet) {
- return <span></span>;
- }
- console.log(wallet);
- const listing = wallet.balances.map((entry) => {
- const av = Amounts.parseOrThrow(entry.available);
- return (
- <p key={av.currency}>
- {bigAmount(av)} {this.formatPending(entry)}
- </p>
- );
- });
- return listing.length > 0 ? (
- <div className="balance">{listing}</div>
- ) : (
- <EmptyBalanceView />
- );
- }
-}
-
-interface TransactionAmountProps {
- debitCreditIndicator: "debit" | "credit" | "unknown";
- amount: AmountString | "unknown";
- pending: boolean;
-}
-
-function TransactionAmount(props: TransactionAmountProps): JSX.Element {
- const [currency, amount] = props.amount.split(":");
- let sign: string;
- switch (props.debitCreditIndicator) {
- case "credit":
- sign = "+";
- break;
- case "debit":
- sign = "-";
- break;
- case "unknown":
- sign = "";
- }
- const style: JSX.AllCSSProperties = {
- marginLeft: "auto",
- display: "flex",
- flexDirection: "column",
- alignItems: "center",
- alignSelf: "center"
- };
- if (props.pending) {
- style.color = "gray";
- }
- return (
- <div style={{ ...style }}>
- <div style={{ fontSize: "x-large" }}>
- {sign}
- {amount}
- </div>
- <div>{currency}</div>
- </div>
- );
-}
-
-interface TransactionLayoutProps {
- debitCreditIndicator: "debit" | "credit" | "unknown";
- amount: AmountString | "unknown";
- timestamp: Timestamp;
- title: string;
- id: string;
- subtitle: string;
- iconPath: string;
- pending: boolean;
-}
-
-function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
- const date = new Date(props.timestamp.t_ms);
- const dateStr = date.toLocaleString([], {
- dateStyle: "medium",
- timeStyle: "short",
- } as any);
- return (
- <div
- style={{
- display: "flex",
- flexDirection: "row",
- border: "1px solid gray",
- borderRadius: "0.5em",
- margin: "0.5em 0",
- justifyContent: "space-between",
- padding: "0.5em",
- }}
- >
- <img src={props.iconPath} />
- <div
- style={{ display: "flex", flexDirection: "column", marginLeft: "1em" }}
- >
- <div style={{ fontSize: "small", color: "gray" }}>{dateStr}</div>
- <div style={{ fontVariant: "small-caps", fontSize: "x-large" }}>
- <a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a>
- {props.pending ? (
- <span style={{ color: "darkblue" }}> (Pending)</span>
- ) : null}
- </div>
-
- <div>{props.subtitle}</div>
- </div>
- <TransactionAmount
- pending={props.pending}
- amount={props.amount}
- debitCreditIndicator={props.debitCreditIndicator}
- />
- </div>
- );
-}
-
-function TransactionItem(props: { tx: Transaction }): JSX.Element {
- const tx = props.tx;
- switch (tx.type) {
- case TransactionType.Withdrawal:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"credit"}
- title="Withdrawal"
- subtitle={`via ${tx.exchangeBaseUrl}`}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-bank-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- case TransactionType.Payment:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"debit"}
- title="Payment"
- subtitle={tx.info.summary}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-shopping-cart-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- case TransactionType.Refund:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"credit"}
- title="Refund"
- subtitle={tx.info.summary}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-refund-2-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- case TransactionType.Tip:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"credit"}
- title="Tip"
- subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-hand-heart-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- case TransactionType.Refresh:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"credit"}
- title="Refresh"
- subtitle={`via exchange ${tx.exchangeBaseUrl}`}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-refresh-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- case TransactionType.Deposit:
- return (
- <TransactionLayout
- id={tx.transactionId}
- amount={tx.amountEffective}
- debitCreditIndicator={"debit"}
- title="Refresh"
- subtitle={`to ${tx.targetPaytoUri}`}
- timestamp={tx.timestamp}
- iconPath="/static/img/ri-refresh-line.svg"
- pending={tx.pending}
- ></TransactionLayout>
- );
- }
-}
-
-export function WalletHistory(props: any): JSX.Element {
- const [transactions, setTransactions] = useState<
- TransactionsResponse | undefined
- >(undefined);
-
- useEffect(() => {
- const fetchData = async (): Promise<void> => {
- const res = await wxApi.getTransactions();
- setTransactions(res);
- };
- fetchData();
- }, []);
-
- if (!transactions) {
- return <div>Loading ...</div>;
- }
-
- const txs = [...transactions.transactions].reverse();
-
- return (
- <div>
- {txs.map((tx, i) => (
- <TransactionItem key={i} tx={tx} />
- ))}
- </div>
- );
-}
-
-interface WalletTransactionProps {
- transaction?: Transaction,
- onDelete: () => void,
- onBack: () => void,
-}
-
-export function WalletTransactionView({ transaction, onDelete, onBack }: WalletTransactionProps) {
- if (!transaction) {
- return <div><i18n.Translate>Loading ...</i18n.Translate></div>;
- }
-
- function Footer() {
- return <footer style={{ marginTop: 'auto', display: 'flex' }}>
- <button onClick={onBack}><i18n.Translate>back</i18n.Translate></button>
- <div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}>
- <button onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button>
-
- </div>
-
- </footer>
- }
-
- function Pending() {
- if (!transaction?.pending) return null
- return <span style={{ fontWeight: 'normal', fontSize: 16, color: 'gray' }}>(pending...)</span>
- }
-
- if (transaction.type === TransactionType.Withdrawal) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Withdrawal <Pending /></h1>
- <p>
- From <b>{transaction.exchangeBaseUrl}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Amount subtracted</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Amount received</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>Exchange fee</td>
- <td>{Amounts.stringify(
- Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
- Amounts.parseOrThrow(transaction.amountEffective),
- ).amount
- )}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
- if (transaction.type === TransactionType.Payment) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Payment ({transaction.proposalId.substring(0, 10)}...) <Pending /></h1>
- <p>
- To <b>{transaction.info.merchant.name}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Order id</td>
- <td>{transaction.info.orderId}</td>
- </tr>
- <tr>
- <td>Summary</td>
- <td>{transaction.info.summary}</td>
- </tr>
- {transaction.info.products && transaction.info.products.length > 0 &&
- <tr>
- <td>Products</td>
- <td><ol style={{ margin: 0, textAlign: 'left' }}>
- {transaction.info.products.map(p =>
- <li>{p.description}</li>
- )}</ol></td>
- </tr>
- }
- <tr>
- <td>Order amount</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Order amount and fees</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>Exchange fee</td>
- <td>{Amounts.stringify(
- Amounts.sub(
- Amounts.parseOrThrow(transaction.amountEffective),
- Amounts.parseOrThrow(transaction.amountRaw),
- ).amount
- )}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
- if (transaction.type === TransactionType.Deposit) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Deposit ({transaction.depositGroupId}) <Pending /></h1>
- <p>
- To <b>{transaction.targetPaytoUri}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Amount deposit</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Amount deposit and fees</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>Exchange fee</td>
- <td>{Amounts.stringify(
- Amounts.sub(
- Amounts.parseOrThrow(transaction.amountEffective),
- Amounts.parseOrThrow(transaction.amountRaw),
- ).amount
- )}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
- if (transaction.type === TransactionType.Refresh) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Refresh <Pending /></h1>
- <p>
- From <b>{transaction.exchangeBaseUrl}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Amount refreshed</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Fees</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
- if (transaction.type === TransactionType.Tip) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Tip <Pending /></h1>
- <p>
- From <b>{transaction.merchantBaseUrl}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Amount deduce</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Amount received</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>Exchange fee</td>
- <td>{Amounts.stringify(
- Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
- Amounts.parseOrThrow(transaction.amountEffective),
- ).amount
- )}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
- const TRANSACTION_FROM_REFUND = /[a-z]*:([\w]{10}).*/
- if (transaction.type === TransactionType.Refund) {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
- <section>
- <h1>Refund ({TRANSACTION_FROM_REFUND.exec(transaction.refundedTransactionId)![1]}...) <Pending /></h1>
- <p>
- From <b>{transaction.info.merchant.name}</b>
- </p>
- <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
- <tr>
- <td>Order id</td>
- <td>{transaction.info.orderId}</td>
- </tr>
- <tr>
- <td>Summary</td>
- <td>{transaction.info.summary}</td>
- </tr>
- {transaction.info.products && transaction.info.products.length > 0 &&
- <tr>
- <td>Products</td>
- <td><ol>
- {transaction.info.products.map(p =>
- <li>{p.description}</li>
- )}</ol></td>
- </tr>
- }
- <tr>
- <td>Amount deduce</td>
- <td>{transaction.amountRaw}</td>
- </tr>
- <tr>
- <td>Amount received</td>
- <td>{transaction.amountEffective}</td>
- </tr>
- <tr>
- <td>Exchange fee</td>
- <td>{Amounts.stringify(
- Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
- Amounts.parseOrThrow(transaction.amountEffective),
- ).amount
- )}</td>
- </tr>
- <tr>
- <td>When</td>
- <td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
- </tr>
- </table>
- </section>
- <Footer />
- </div>
- );
- }
-
-
- return <div></div>
-}
-
-export function WalletTransaction({ tid }: { tid: string }): JSX.Element {
- const [transaction, setTransaction] = useState<
- Transaction | undefined
- >(undefined);
-
- useEffect(() => {
- const fetchData = async (): Promise<void> => {
- const res = await wxApi.getTransactions();
- const ts = res.transactions.filter(t => t.transactionId === tid)
- if (ts.length === 1) {
- setTransaction(ts[0]);
- } else {
- route(Pages.history)
- }
- };
- fetchData();
- }, []);
-
- return <WalletTransactionView
- transaction={transaction}
- onDelete={() => wxApi.deleteTransaction(tid).then(_ => history.go(-1))}
- onBack={() => { history.go(-1) }}
- />
-}
-
-export function WalletSettings() {
- const [permissionsEnabled, togglePermissions] = useExtendedPermissions()
- return (
- <div>
- <h2>Permissions</h2>
- <PermissionsCheckbox enabled={permissionsEnabled} onToggle={togglePermissions} />
- {/*
- <h2>Developer mode</h2>
- <DebugCheckbox enabled={permissionsEnabled} onToggle={togglePermissions} />
- */}
- </div>
- );
-}
-
-
-export function DebugCheckbox({ enabled, onToggle }: { enabled: boolean, onToggle: () => void }): JSX.Element {
- return (
- <div>
- <input
- checked={enabled}
- onClick={onToggle}
- type="checkbox"
- id="checkbox-perm"
- style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }}
- />
- <label
- htmlFor="checkbox-perm"
- style={{ marginLeft: "0.5em", fontWeight: "bold" }}
- >
- Automatically open wallet based on page content
- </label>
- <span
- style={{
- color: "#383838",
- fontSize: "smaller",
- display: "block",
- marginLeft: "2em",
- }}
- >
- (Enabling this option below will make using the wallet faster, but
- requires more permissions from your browser.)
- </span>
- </div>
- );
-}
-
-function reload(): void {
- try {
- chrome.runtime.reload();
- window.close();
- } catch (e) {
- // Functionality missing in firefox, ignore!
- }
-}
-
-async function confirmReset(): Promise<void> {
- if (
- confirm(
- "Do you want to IRREVOCABLY DESTROY everything inside your" +
- " wallet and LOSE ALL YOUR COINS?",
- )
- ) {
- await wxApi.resetDb();
- window.close();
- }
-}
-
-export function WalletDebug(props: any): JSX.Element {
- return (
- <div>
- <p>Debug tools:</p>
- <button onClick={openExtensionPage("/static/popup.html")}>wallet tab</button>
- <br />
- <button onClick={confirmReset}>reset</button>
- <button onClick={reload}>reload chrome extension</button>
- <Diagnostics />
- </div>
- );
-}
-
-function openExtensionPage(page: string) {
- return () => {
- chrome.tabs.create({
- url: chrome.extension.getURL(page),
- });
- };
-}
-
-// function openTab(page: string) {
-// return (evt: React.SyntheticEvent<any>) => {
-// evt.preventDefault();
-// chrome.tabs.create({
-// url: page,
-// });
-// };
-// }
-
-function makeExtensionUrlWithParams(
- url: string,
- params?: { [name: string]: string | undefined },
-): string {
- const innerUrl = new URL(chrome.extension.getURL("/" + url));
- if (params) {
- for (const key in params) {
- const p = params[key];
- if (p) {
- innerUrl.searchParams.set(key, p);
- }
- }
- }
- return innerUrl.href;
-}
-
-export function actionForTalerUri(talerUri: string): string | undefined {
- const uriType = classifyTalerUri(talerUri);
- 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;
-}
-
-export async function findTalerUriInActiveTab(): Promise<string | undefined> {
- return new Promise((resolve, reject) => {
- chrome.tabs.executeScript(
- {
- code: `
- (() => {
- let x = document.querySelector("a[href^='taler://'") || document.querySelector("a[href^='taler+http://'");
- return x ? x.href.toString() : null;
- })();
- `,
- allFrames: false,
- },
- (result) => {
- if (chrome.runtime.lastError) {
- console.error(chrome.runtime.lastError);
- resolve(undefined);
- return;
- }
- console.log("got result", result);
- resolve(result[0]);
- },
- );
- });
-}
-
-// export function WalletPopup(): JSX.Element {
-// const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
-// undefined,
-// );
-// const [dismissed, setDismissed] = useState(false);
-// useEffect(() => {
-// async function check(): Promise<void> {
-// const talerUri = await findTalerUriInActiveTab();
-// if (talerUri) {
-// const actionUrl = actionForTalerUri(talerUri);
-// setTalerActionUrl(actionUrl);
-// }
-// }
-// check();
-// }, []);
-// if (talerActionUrl && !dismissed) {
-// return (
-// <div style={{ padding: "1em", width: 400 }}>
-// <h1>Taler Action</h1>
-// <p>This page has a Taler action. </p>
-// <p>
-// <button
-// onClick={() => {
-// window.open(talerActionUrl, "_blank");
-// }}
-// >
-// Open
-// </button>
-// </p>
-// <p>
-// <button onClick={() => setDismissed(true)}>Dismiss</button>
-// </p>
-// </div>
-// );
-// }
-// return (
-// <div>
-// <Match>{({ path }: any) => <WalletNavBar current={path} />}</Match>
-// <div style={{ margin: "1em", width: 400 }}>
-// <Router>
-// <Route path={Pages.balance} component={WalletBalanceView} />
-// <Route path={Pages.settings} component={WalletSettings} />
-// <Route path={Pages.debug} component={WalletDebug} />
-// <Route path={Pages.history} component={WalletHistory} />
-// <Route path={Pages.transaction} component={WalletTransaction} />
-// </Router>
-// </div>
-// </div>
-// );
-// }
-
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 926ae7aa7..6cc781aa7 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -23,13 +23,17 @@
import { render } from "preact";
import { setupI18n } from "@gnu-taler/taler-util";
import { strings } from "./i18n/strings";
-import { useEffect, useState } from "preact/hooks";
+import { useEffect } from "preact/hooks";
import {
- actionForTalerUri, findTalerUriInActiveTab, Pages, WalletBalanceView, WalletDebug, WalletHistory,
- WalletNavBar, WalletSettings, WalletTransaction, WalletTransactionView
-} from "./popup/popup";
+ Pages, WalletNavBar} from "./popup/popup";
+import { HistoryPage } from "./popup/History";
+import { DebugPage } from "./popup/Debug";
+import { SettingsPage } from "./popup/Settings";
+import { TransactionPage } from "./popup/Transaction";
+import { BalancePage } from "./popup/Balance";
import Match from "preact-router/match";
import Router, { route, Route } from "preact-router";
+import { useTalerActionURL } from "./hooks/useTalerActionURL";
// import { Application } from "./Application";
function main(): void {
@@ -53,25 +57,6 @@ if (document.readyState === "loading") {
main();
}
-function useTalerActionURL(): [string | undefined, (s: boolean) => void] {
- const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
- undefined,
- );
- const [dismissed, setDismissed] = useState(false);
- useEffect(() => {
- async function check(): Promise<void> {
- const talerUri = await findTalerUriInActiveTab();
- if (talerUri) {
- const actionUrl = actionForTalerUri(talerUri);
- setTalerActionUrl(actionUrl);
- }
- }
- check();
- }, []);
- const url = dismissed ? undefined : talerActionUrl
- return [url, setDismissed]
-}
-
interface Props {
url: string;
onDismiss: (s: boolean) => void;
@@ -105,11 +90,11 @@ function Application() {
<Match>{({ path }: any) => <WalletNavBar current={path} />}</Match >
<div style={{ margin: "1em", width: 400 }}>
<Router>
- <Route path={Pages.balance} component={WalletBalanceView} />
- <Route path={Pages.settings} component={WalletSettings} />
- <Route path={Pages.debug} component={WalletDebug} />
- <Route path={Pages.history} component={WalletHistory} />
- <Route path={Pages.transaction} component={WalletTransaction} />
+ <Route path={Pages.balance} component={BalancePage} />
+ <Route path={Pages.settings} component={SettingsPage} />
+ <Route path={Pages.debug} component={DebugPage} />
+ <Route path={Pages.history} component={HistoryPage} />
+ <Route path={Pages.transaction} component={TransactionPage} />
<Route default component={Redirect} to={Pages.balance} />
</Router>
</div>
@@ -118,8 +103,6 @@ function Application() {
}
-
-
function Redirect({ to }: { to: string }): null {
useEffect(() => {
route(to, true)
diff --git a/packages/taler-wallet-webextension/src/wallet/pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
index e958cd484..23b4e6c1a 100644
--- a/packages/taler-wallet-webextension/src/wallet/pay.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
@@ -45,7 +45,7 @@ interface Props {
talerPayUri?: string
}
-export function TalerPayDialog({ talerPayUri }: Props): JSX.Element {
+export function PayPage({ talerPayUri }: Props): JSX.Element {
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined);
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
@@ -222,14 +222,3 @@ export function TalerPayDialog({ talerPayUri }: Props): JSX.Element {
);
}
-/**
- * @deprecated to be removed
- */
-export function createPayPage(): JSX.Element {
- const url = new URL(document.location.href);
- const talerPayUri = url.searchParams.get("talerPayUri");
- if (!talerPayUri) {
- throw Error("invalid parameter");
- }
- return <TalerPayDialog talerPayUri={talerPayUri} />;
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/refund.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.tsx
index 1991bc9d8..702217415 100644
--- a/packages/taler-wallet-webextension/src/wallet/refund.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Refund.tsx
@@ -33,7 +33,7 @@ interface Props {
talerRefundUri?: string
}
-export function RefundStatusView({ talerRefundUri }: Props): JSX.Element {
+export function RefundPage({ talerRefundUri }: Props): JSX.Element {
const [applyResult, setApplyResult] = useState<ApplyRefundResponse | undefined>(undefined);
const [errMsg, setErrMsg] = useState<string | undefined>(undefined);
@@ -87,22 +87,3 @@ export function RefundStatusView({ talerRefundUri }: Props): JSX.Element {
</>
);
}
-
-/**
- * @deprecated to be removed
- */
-export function createRefundPage(): JSX.Element {
- const url = new URL(document.location.href);
-
- const container = document.getElementById("container");
- if (!container) {
- throw Error("fatal: can't mount component, container missing");
- }
-
- const talerRefundUri = url.searchParams.get("talerRefundUri");
- if (!talerRefundUri) {
- throw Error("taler refund URI required");
- }
-
- return <RefundStatusView talerRefundUri={talerRefundUri} />;
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/tip.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.tsx
index d832976d8..708e8940b 100644
--- a/packages/taler-wallet-webextension/src/wallet/tip.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Tip.tsx
@@ -30,7 +30,7 @@ interface Props {
talerTipUri?: string
}
-export function TalerTipDialog({ talerTipUri }: Props): JSX.Element {
+export function TipPage({ talerTipUri }: Props): JSX.Element {
const [updateCounter, setUpdateCounter] = useState<number>(0);
const [prepareTipResult, setPrepareTipResult] = useState<
PrepareTipResult | undefined
@@ -95,15 +95,3 @@ export function TalerTipDialog({ talerTipUri }: Props): JSX.Element {
);
}
}
-
-/**
- * @deprecated to be removed
- */
-export function createTipPage(): JSX.Element {
- const url = new URL(document.location.href);
- const talerTipUri = url.searchParams.get("talerTipUri");
- if (!talerTipUri) {
- throw Error("invalid parameter");
- }
- return <TalerTipDialog talerTipUri={talerTipUri} />;
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index 9be62bf8b..0f9cc8677 100644
--- a/packages/taler-wallet-webextension/src/wallet/welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -20,43 +20,12 @@
* @author Florian Dold
*/
-import * as wxApi from "../wxApi";
-import { getPermissionsApi } from "../compat";
-import { extendedPermissions } from "../permissions";
-import { Fragment, JSX } from "preact/jsx-runtime";
+import { JSX } from "preact/jsx-runtime";
import { PermissionsCheckbox } from "../components/PermissionsCheckbox";
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
import { Diagnostics } from "../components/Diagnostics";
-export async function handleExtendedPerm(isEnabled: boolean): Promise<boolean> {
- let nextVal: boolean | undefined;
-
- if (!isEnabled) {
- const granted = await new Promise<boolean>((resolve, reject) => {
- // We set permissions here, since apparently FF wants this to be done
- // as the result of an input event ...
- getPermissionsApi().request(extendedPermissions, (granted: boolean) => {
- if (chrome.runtime.lastError) {
- console.error("error requesting permissions");
- console.error(chrome.runtime.lastError);
- reject(chrome.runtime.lastError);
- return;
- }
- console.log("permissions granted:", granted);
- resolve(granted);
- });
- });
- const res = await wxApi.setExtendedPermissions(granted);
- nextVal = res.newValue;
- } else {
- const res = await wxApi.setExtendedPermissions(false);
- nextVal = res.newValue;
- }
- console.log("new permissions applied:", nextVal ?? false);
- return nextVal ?? false
-}
-
-export function Welcome(): JSX.Element {
+export function WelcomePage(): JSX.Element {
const [permissionsEnabled, togglePermissions] = useExtendedPermissions()
return (
<>
@@ -74,10 +43,3 @@ export function Welcome(): JSX.Element {
</>
);
}
-
-/**
- * @deprecated to be removed
- */
-export function createWelcomePage(): JSX.Element {
- return <Welcome />;
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/withdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx
index 86f0eec90..24fb17dfa 100644
--- a/packages/taler-wallet-webextension/src/wallet/withdraw.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx
@@ -20,7 +20,7 @@
*/
import { h } from 'preact';
-import { View, ViewProps } from './withdraw';
+import { View, ViewProps } from './Withdraw';
export default {
diff --git a/packages/taler-wallet-webextension/src/wallet/withdraw.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx
index cb96fa4df..5dc12407b 100644
--- a/packages/taler-wallet-webextension/src/wallet/withdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx
@@ -111,7 +111,7 @@ export function View({ talerWithdrawUri, details, cancelled, selectedExchange, a
)
}
-export function WithdrawalDialog({ talerWithdrawUri }: Props): JSX.Element {
+export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined);
const [selectedExchange, setSelectedExchange] = useState<
string | undefined
@@ -159,15 +159,3 @@ export function WithdrawalDialog({ talerWithdrawUri }: Props): JSX.Element {
/>
}
-
-/**
- * @deprecated to be removed
- */
-export function createWithdrawPage(): JSX.Element {
- const url = new URL(document.location.href);
- const talerWithdrawUri = url.searchParams.get("talerWithdrawUri");
- if (!talerWithdrawUri) {
- throw Error("withdraw URI required");
- }
- return <WithdrawalDialog talerWithdrawUri={talerWithdrawUri} />;
-}
diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
index 2d1671dd4..cb97ffbeb 100644
--- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
@@ -25,11 +25,11 @@ import { setupI18n } from "@gnu-taler/taler-util";
import { strings } from "./i18n/strings";
import { createHashHistory } from 'history';
-import { WithdrawalDialog } from "./wallet/withdraw";
-import { Welcome } from "./wallet/welcome";
-import { TalerPayDialog } from "./wallet/pay";
-import { RefundStatusView } from "./wallet/refund";
-import { TalerTipDialog } from './wallet/tip';
+import { WithdrawPage } from "./wallet/Withdraw";
+import { WelcomePage } from "./wallet/Welcome";
+import { PayPage } from "./wallet/Pay";
+import { RefundPage } from "./wallet/Refund";
+import { TipPage } from './wallet/Tip';
import Router, { route, Route } from "preact-router";
@@ -82,7 +82,7 @@ function Application() {
</div>
<h1>Browser Extension Installed!</h1>
<div>
- <Welcome />
+ <WelcomePage />
</div>
</section>
}} />
@@ -91,7 +91,7 @@ function Application() {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<article class="fade">
- <TalerPayDialog talerPayUri={queryParams.talerPayUri} />
+ <PayPage talerPayUri={queryParams.talerPayUri} />
</article>
</section>
}} />
@@ -100,7 +100,7 @@ function Application() {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<article class="fade">
- <RefundStatusView talerRefundUri={queryParams.talerRefundUri} />
+ <RefundPage talerRefundUri={queryParams.talerRefundUri} />
</article>
</section>
}} />
@@ -109,7 +109,7 @@ function Application() {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<div>
- <TalerTipDialog talerTipUri={queryParams.talerTipUri} />
+ <TipPage talerTipUri={queryParams.talerTipUri} />
</div>
</section>
}} />
@@ -121,7 +121,7 @@ function Application() {
</h1>
</div>
<div class="fade">
- <WithdrawalDialog talerWithdrawUri={queryParams.talerWithdrawUri} />
+ <WithdrawPage talerWithdrawUri={queryParams.talerWithdrawUri} />
</div>
</section>
}} />