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
+ */
+
+ import { JSX } from "preact";
+
+export function DebugCheckbox({ enabled, onToggle }: { enabled: boolean; onToggle: () => void; }): JSX.Element {
+ return (
+
+
+
+
+ (Enabling this option below will make using the wallet faster, but
+ requires more permissions from your browser.)
+
+
+ );
+}
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 {
+ const res = await wxApi.getExtendedPermissions();
+ setEnabled(res.newValue);
+ }
+ getExtendedPermValue();
+ }, []);
+ return [enabled, toggle];
+}
+
+async function handleExtendedPerm(isEnabled: boolean): Promise {
+ let nextVal: boolean | undefined;
+
+ if (!isEnabled) {
+ const granted = await new Promise((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 {
- 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(
+ undefined
+ );
+ const [dismissed, setDismissed] = useState(false);
+ useEffect(() => {
+ async function check(): Promise {
+ 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 {
+ 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
+ */
+
+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 (
+
+ {v}{" "}
+ {amount.currency}
+
+ );
+}
+
+function EmptyBalanceView(): JSX.Element {
+ return (
+
+ You have no balance to show. Need some{" "}
+ help getting started?
+
+ ) : (
+
+ );
+ }
+}
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
+ */
+
+import { JSX } from "preact";
+import { Diagnostics } from "../components/Diagnostics";
+import * as wxApi from "../wxApi";
+
+
+export function DebugPage(props: any): JSX.Element {
+ return (
+
+
Debug tools:
+
+
+
+
+
+
+ );
+}
+
+export function reload(): void {
+ try {
+ chrome.runtime.reload();
+ window.close();
+ } catch (e) {
+ // Functionality missing in firefox, ignore!
+ }
+}
+
+export async function confirmReset(): Promise {
+ 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
+ */
+
+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 => {
+ const res = await wxApi.getTransactions();
+ setTransactions(res);
+ };
+ fetchData();
+ }, []);
+
+ if (!transactions) {
+ return
+ );
+}
+
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
+*/
+
+
+import { PermissionsCheckbox } from "../components/PermissionsCheckbox";
+import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
+
+
+export function SettingsPage() {
+ const [permissionsEnabled, togglePermissions] = useExtendedPermissions();
+ return (
+
+
Permissions
+
+ {/*
+
Developer mode
+
+ */}
+
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/popup/popup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
similarity index 79%
rename from packages/taler-wallet-webextension/src/popup/popup.stories.tsx
rename to 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) =>
@@ -114,32 +115,32 @@ const exampleData = {
} as TransactionRefund,
}
-function dynamic(props: any) {
+function createExample(Component: FunctionalComponent, props: Partial) {
const r = (args: any) =>
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
+ */
+
+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 => {
+ 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 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
Loading ...
;
+ }
+
+ function Footer() {
+ return
+ }
+
+ function Pending() {
+ if (!transaction?.pending) return null
+ return (pending...)
+ }
+
+ if (transaction.type === TransactionType.Withdrawal) {
+ return (
+