summaryrefslogtreecommitdiff
path: root/src/webex/pages
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-08-03 13:00:48 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-08-03 13:01:05 +0530
commitffd2a62c3f7df94365980302fef3bc3376b48182 (patch)
tree270af6f16b4cc7f5da2afdba55c8bc9dbea5eca5 /src/webex/pages
parentaa481e42675fb7c4dcbbeec0ba1c61e1953b9596 (diff)
downloadwallet-core-ffd2a62c3f7df94365980302fef3bc3376b48182.tar.gz
wallet-core-ffd2a62c3f7df94365980302fef3bc3376b48182.tar.bz2
wallet-core-ffd2a62c3f7df94365980302fef3bc3376b48182.zip
modularize repo, use pnpm, improve typechecking
Diffstat (limited to 'src/webex/pages')
-rw-r--r--src/webex/pages/add-auditor.tsx135
-rw-r--r--src/webex/pages/auditors.tsx161
-rw-r--r--src/webex/pages/benchmark.tsx104
-rw-r--r--src/webex/pages/pay.tsx182
-rw-r--r--src/webex/pages/payback.tsx30
-rw-r--r--src/webex/pages/popup.tsx499
-rw-r--r--src/webex/pages/refund.tsx89
-rw-r--r--src/webex/pages/reset-required.tsx93
-rw-r--r--src/webex/pages/return-coins.tsx30
-rw-r--r--src/webex/pages/tip.tsx103
-rw-r--r--src/webex/pages/welcome.tsx190
-rw-r--r--src/webex/pages/withdraw.tsx229
12 files changed, 0 insertions, 1845 deletions
diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx
deleted file mode 100644
index c28d15cad..000000000
--- a/src/webex/pages/add-auditor.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Inria
-
- 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/>
- */
-
-/**
- * View and edit auditors.
- *
- * @author Florian Dold
- */
-
-import { CurrencyRecord } from "../../types/dbTypes";
-import { getCurrencies, updateCurrency } from "../wxApi";
-import React, { useState } from "react";
-
-interface ConfirmAuditorProps {
- url: string;
- currency: string;
- auditorPub: string;
- expirationStamp: number;
-}
-
-function ConfirmAuditor(props: ConfirmAuditorProps): JSX.Element {
- const [addDone, setAddDone] = useState(false);
-
- const add = async (): Promise<void> => {
- const currencies = await getCurrencies();
- let currency: CurrencyRecord | undefined;
-
- for (const c of currencies) {
- if (c.name === props.currency) {
- currency = c;
- }
- }
-
- if (!currency) {
- currency = {
- name: props.currency,
- auditors: [],
- fractionalDigits: 2,
- exchanges: [],
- };
- }
-
- const newAuditor = {
- auditorPub: props.auditorPub,
- baseUrl: props.url,
- expirationStamp: props.expirationStamp,
- };
-
- let auditorFound = false;
- for (const idx in currency.auditors) {
- const a = currency.auditors[idx];
- if (a.baseUrl === props.url) {
- auditorFound = true;
- // Update auditor if already found by URL.
- currency.auditors[idx] = newAuditor;
- }
- }
-
- if (!auditorFound) {
- currency.auditors.push(newAuditor);
- }
-
- await updateCurrency(currency);
-
- setAddDone(true);
- };
-
- const back = (): void => {
- window.history.back();
- };
-
- return (
- <div id="main">
- <p>
- Do you want to let <strong>{props.auditorPub}</strong> audit the
- currency &quot;{props.currency}&quot;?
- </p>
- {addDone ? (
- <div>
- Auditor was added! You can also{" "}
- <a href={chrome.extension.getURL("/auditors.html")}>view and edit</a>{" "}
- auditors.
- </div>
- ) : (
- <div>
- <button
- onClick={() => add()}
- className="pure-button pure-button-primary"
- >
- Yes
- </button>
- <button onClick={() => back()} className="pure-button">
- No
- </button>
- </div>
- )}
- </div>
- );
-}
-
-export function makeAddAuditorPage(): JSX.Element {
- const walletPageUrl = new URL(document.location.href);
- const url = walletPageUrl.searchParams.get("url");
- if (!url) {
- throw Error("missign parameter (url)");
- }
- const currency = walletPageUrl.searchParams.get("currency");
- if (!currency) {
- throw Error("missing parameter (currency)");
- }
- const auditorPub = walletPageUrl.searchParams.get("auditorPub");
- if (!auditorPub) {
- throw Error("missing parameter (auditorPub)");
- }
- const auditorStampStr = walletPageUrl.searchParams.get("expirationStamp");
- if (!auditorStampStr) {
- throw Error("missing parameter (auditorStampStr)");
- }
- const expirationStamp = Number.parseInt(auditorStampStr);
- const args = { url, currency, auditorPub, expirationStamp };
- return <ConfirmAuditor {...args} />;
-}
diff --git a/src/webex/pages/auditors.tsx b/src/webex/pages/auditors.tsx
deleted file mode 100644
index ac93afd31..000000000
--- a/src/webex/pages/auditors.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Inria
-
- 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/>
- */
-
-/**
- * View and edit auditors.
- *
- * @author Florian Dold
- */
-
-import {
- AuditorRecord,
- CurrencyRecord,
- ExchangeForCurrencyRecord,
-} from "../../types/dbTypes";
-
-import { getCurrencies, updateCurrency } from "../wxApi";
-
-import * as React from "react";
-
-interface CurrencyListState {
- currencies?: CurrencyRecord[];
-}
-
-class CurrencyList extends React.Component<{}, CurrencyListState> {
- constructor(props: {}) {
- super(props);
- const port = chrome.runtime.connect();
- port.onMessage.addListener((msg: any) => {
- if (msg.notify) {
- console.log("got notified");
- this.update();
- }
- });
- this.update();
- this.state = {} as any;
- }
-
- async update(): Promise<void> {
- const currencies = await getCurrencies();
- console.log("currencies: ", currencies);
- this.setState({ currencies });
- }
-
- async confirmRemoveAuditor(
- c: CurrencyRecord,
- a: AuditorRecord,
- ): Promise<void> {
- if (
- window.confirm(
- `Do you really want to remove auditor ${a.baseUrl} for currency ${c.name}?`,
- )
- ) {
- c.auditors = c.auditors.filter((x) => x.auditorPub !== a.auditorPub);
- await updateCurrency(c);
- }
- }
-
- async confirmRemoveExchange(
- c: CurrencyRecord,
- e: ExchangeForCurrencyRecord,
- ): Promise<void> {
- if (
- window.confirm(
- `Do you really want to remove exchange ${e.baseUrl} for currency ${c.name}?`,
- )
- ) {
- c.exchanges = c.exchanges.filter((x) => x.baseUrl !== e.baseUrl);
- await updateCurrency(c);
- }
- }
-
- renderAuditors(c: CurrencyRecord): any {
- if (c.auditors.length === 0) {
- return <p>No trusted auditors for this currency.</p>;
- }
- return (
- <div>
- <p>Trusted Auditors:</p>
- <ul>
- {c.auditors.map((a) => (
- <li key={a.baseUrl}>
- {a.baseUrl}{" "}
- <button
- className="pure-button button-destructive"
- onClick={() => this.confirmRemoveAuditor(c, a)}
- >
- Remove
- </button>
- <ul>
- <li>valid until {new Date(a.expirationStamp).toString()}</li>
- <li>public key {a.auditorPub}</li>
- </ul>
- </li>
- ))}
- </ul>
- </div>
- );
- }
-
- renderExchanges(c: CurrencyRecord): any {
- if (c.exchanges.length === 0) {
- return <p>No trusted exchanges for this currency.</p>;
- }
- return (
- <div>
- <p>Trusted Exchanges:</p>
- <ul>
- {c.exchanges.map((e) => (
- <li key={e.baseUrl}>
- {e.baseUrl}{" "}
- <button
- className="pure-button button-destructive"
- onClick={() => this.confirmRemoveExchange(c, e)}
- >
- Remove
- </button>
- </li>
- ))}
- </ul>
- </div>
- );
- }
-
- render(): JSX.Element {
- const currencies = this.state.currencies;
- if (!currencies) {
- return <span>...</span>;
- }
- return (
- <div id="main">
- {currencies.map((c) => (
- <div key={c.name}>
- <h1>Currency {c.name}</h1>
- <p>Displayed with {c.fractionalDigits} fractional digits.</p>
- <h2>Auditors</h2>
- <div>{this.renderAuditors(c)}</div>
- <h2>Exchanges</h2>
- <div>{this.renderExchanges(c)}</div>
- </div>
- ))}
- </div>
- );
- }
-}
-
-export function makeAuditorsPage(): JSX.Element {
- return <CurrencyList />;
-}
diff --git a/src/webex/pages/benchmark.tsx b/src/webex/pages/benchmark.tsx
deleted file mode 100644
index eb7193e0c..000000000
--- a/src/webex/pages/benchmark.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 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/>
- */
-
-/**
- * Benchmarks for the wallet.
- *
- * @author Florian Dold
- */
-
-import * as i18n from "../i18n";
-
-import { BenchmarkResult } from "../../types/walletTypes";
-
-import * as wxApi from "../wxApi";
-
-import * as React from "react";
-
-interface BenchmarkRunnerState {
- repetitions: number;
- result?: BenchmarkResult;
- running: boolean;
-}
-
-function BenchmarkDisplay(props: BenchmarkRunnerState): JSX.Element {
- const result = props.result;
- if (!result) {
- if (props.running) {
- return <div>Waiting for results ...</div>;
- } else {
- return <div></div>;
- }
- }
- return (
- <>
- <h2>Results for {result.repetitions} repetitions</h2>
- <table className="pure-table">
- <thead>
- <tr>
- <th>{i18n.str`Operation`}</th>
- <th>{i18n.str`time (ms/op)`}</th>
- </tr>
- {Object.keys(result.time)
- .sort()
- .map((k) => (
- <tr key={k}>
- <td>{k}</td>
- <td>{result.time[k] / result.repetitions}</td>
- </tr>
- ))}
- </thead>
- </table>
- </>
- );
-}
-
-class BenchmarkRunner extends React.Component<any, BenchmarkRunnerState> {
- constructor(props: any) {
- super(props);
- this.state = {
- repetitions: 10,
- running: false,
- };
- }
-
- async run(): Promise<void> {
- this.setState({ result: undefined, running: true });
- const result = await wxApi.benchmarkCrypto(this.state.repetitions);
- this.setState({ result, running: false });
- }
-
- render(): JSX.Element {
- return (
- <div>
- <label>Repetitions:</label>
- <input
- type="number"
- value={this.state.repetitions}
- onChange={(evt) =>
- this.setState({ repetitions: Number.parseInt(evt.target.value) })
- }
- />{" "}
- <button onClick={() => this.run()}>Run</button>
- <BenchmarkDisplay {...this.state} />
- </div>
- );
- }
-}
-
-export function makeBenchmarkPage(): JSX.Element {
- return <BenchmarkRunner />;
-}
diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx
deleted file mode 100644
index ce44c0040..000000000
--- a/src/webex/pages/pay.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 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/>
- */
-
-/**
- * Page shown to the user to confirm entering
- * a contract.
- */
-
-/**
- * Imports.
- */
-import * as i18n from "../i18n";
-
-import { PreparePayResult, PreparePayResultType } from "../../types/walletTypes";
-
-import { renderAmount, ProgressButton } from "../renderHtml";
-import * as wxApi from "../wxApi";
-
-import React, { useState, useEffect } from "react";
-
-import * as Amounts from "../../util/amounts";
-import { codecForContractTerms, ContractTerms } from "../../types/talerTypes";
-
-function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
- const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>();
- const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
- const [numTries, setNumTries] = useState(0);
- const [loading, setLoading] = useState(false);
- let amountEffective: Amounts.AmountJson | undefined = undefined;
-
- useEffect(() => {
- const doFetch = async (): Promise<void> => {
- const p = await wxApi.preparePay(talerPayUri);
- setPayStatus(p);
- };
- doFetch();
- }, [numTries, talerPayUri]);
-
- if (!payStatus) {
- return <span>Loading payment information ...</span>;
- }
-
- let insufficientBalance = false;
- if (payStatus.status == "insufficient-balance") {
- insufficientBalance = true;
- }
-
- if (payStatus.status === "payment-possible") {
- amountEffective = Amounts.parseOrThrow(payStatus.amountEffective);
- }
-
- if (payStatus.status === PreparePayResultType.AlreadyConfirmed && numTries === 0) {
- return (
- <span>
- You have already paid for this article. Click{" "}
- <a href={payStatus.nextUrl}>here</a> to view it again.
- </span>
- );
- }
-
- let contractTerms: ContractTerms;
-
- try {
- contractTerms = codecForContractTerms().decode(payStatus.contractTerms);
- } catch (e) {
- // This should never happen, as the wallet is supposed to check the contract terms
- // before storing them.
- console.error(e);
- console.log("raw contract terms were", payStatus.contractTerms);
- return <span>Invalid contract terms.</span>;
- }
-
- if (!contractTerms) {
- return (
- <span>
- Error: did not get contract terms from merchant or wallet backend.
- </span>
- );
- }
-
- let merchantName: React.ReactElement;
- if (contractTerms.merchant && contractTerms.merchant.name) {
- merchantName = <strong>{contractTerms.merchant.name}</strong>;
- } else {
- merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
- }
-
- const amount = (
- <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong>
- );
-
- const doPayment = async (): Promise<void> => {
- if (payStatus.status !== "payment-possible") {
- throw Error(`invalid state: ${payStatus.status}`);
- }
- const proposalId = payStatus.proposalId;
- setNumTries(numTries + 1);
- try {
- setLoading(true);
- const res = await wxApi.confirmPay(proposalId, undefined);
- document.location.href = res.nextUrl;
- } catch (e) {
- console.error(e);
- setPayErrMsg(e.message);
- }
- };
-
- return (
- <div>
- <p>
- <i18n.Translate wrap="p">
- The merchant <span>{merchantName}</span> offers you to purchase:
- </i18n.Translate>
- <div style={{ textAlign: "center" }}>
- <strong>{contractTerms.summary}</strong>
- </div>
- {amountEffective ? (
- <i18n.Translate wrap="p">
- The total price is <span>{amount} </span>
- (plus <span>{renderAmount(amountEffective)}</span> fees).
- </i18n.Translate>
- ) : (
- <i18n.Translate wrap="p">
- The total price is <span>{amount}</span>.
- </i18n.Translate>
- )}
- </p>
-
- {insufficientBalance ? (
- <div>
- <p style={{ color: "red", fontWeight: "bold" }}>
- Unable to pay: Your balance is insufficient.
- </p>
- </div>
- ) : null}
-
- {payErrMsg ? (
- <div>
- <p>Payment failed: {payErrMsg}</p>
- <button
- className="pure-button button-success"
- onClick={() => doPayment()}
- >
- {i18n.str`Retry`}
- </button>
- </div>
- ) : (
- <div>
- <ProgressButton
- loading={loading}
- disabled={insufficientBalance}
- onClick={() => doPayment()}
- >
- {i18n.str`Confirm payment`}
- </ProgressButton>
- </div>
- )}
- </div>
- );
-}
-
-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/src/webex/pages/payback.tsx b/src/webex/pages/payback.tsx
deleted file mode 100644
index 5d42f5f47..000000000
--- a/src/webex/pages/payback.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Inria
-
- 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/>
- */
-
-/**
- * View and edit auditors.
- *
- * @author Florian Dold
- */
-
-/**
- * Imports.
- */
-import * as React from "react";
-
-export function makePaybackPage(): JSX.Element {
- return <div>not implemented</div>;
-}
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
deleted file mode 100644
index 8a99a6d90..000000000
--- a/src/webex/pages/popup.tsx
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- 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/>
- */
-
-/**
- * Popup shown to the user when they click
- * the Taler browser action button.
- *
- * @author Florian Dold
- */
-
-/**
- * Imports.
- */
-import * as i18n from "../i18n";
-
-import { AmountJson } from "../../util/amounts";
-import * as Amounts from "../../util/amounts";
-
-import { abbrev, renderAmount, PageLink } from "../renderHtml";
-import * as wxApi from "../wxApi";
-
-import React, { Fragment, useState, useEffect } from "react";
-
-import moment from "moment";
-import { Timestamp } from "../../util/time";
-import { classifyTalerUri, TalerUriType } from "../../util/taleruri";
-import { PermissionsCheckbox } from "./welcome";
-import { BalancesResponse, Balance } from "../../types/walletTypes";
-
-// FIXME: move to newer react functions
-/* eslint-disable react/no-deprecated */
-
-class Router extends React.Component<any, any> {
- static setRoute(s: string): void {
- window.location.hash = s;
- }
-
- static getRoute(): string {
- // Omit the '#' at the beginning
- return window.location.hash.substring(1);
- }
-
- static onRoute(f: any): () => void {
- Router.routeHandlers.push(f);
- return () => {
- const i = Router.routeHandlers.indexOf(f);
- this.routeHandlers = this.routeHandlers.splice(i, 1);
- };
- }
-
- private static routeHandlers: any[] = [];
-
- componentWillMount(): void {
- console.log("router mounted");
- window.onhashchange = () => {
- this.setState({});
- for (const f of Router.routeHandlers) {
- f();
- }
- };
- }
-
- render(): JSX.Element {
- const route = window.location.hash.substring(1);
- console.log("rendering route", route);
- let defaultChild: React.ReactChild | null = null;
- let foundChild: React.ReactChild | null = null;
- React.Children.forEach(this.props.children, (child) => {
- const childProps: any = (child as any).props;
- if (!childProps) {
- return;
- }
- if (childProps.default) {
- defaultChild = child as React.ReactChild;
- }
- if (childProps.route === route) {
- foundChild = child as React.ReactChild;
- }
- });
- const c: React.ReactChild | null = foundChild || defaultChild;
- if (!c) {
- throw Error("unknown route");
- }
- Router.setRoute((c as any).props.route);
- return <div>{c}</div>;
- }
-}
-
-interface TabProps {
- target: string;
- children?: React.ReactNode;
-}
-
-function Tab(props: TabProps): JSX.Element {
- let cssClass = "";
- if (props.target === Router.getRoute()) {
- cssClass = "active";
- }
- const onClick = (e: React.MouseEvent<HTMLAnchorElement>): void => {
- Router.setRoute(props.target);
- e.preventDefault();
- };
- return (
- <a onClick={onClick} href={props.target} className={cssClass}>
- {props.children}
- </a>
- );
-}
-
-class WalletNavBar extends React.Component<any, any> {
- private cancelSubscription: any;
-
- componentWillMount(): void {
- this.cancelSubscription = Router.onRoute(() => {
- this.setState({});
- });
- }
-
- componentWillUnmount(): void {
- if (this.cancelSubscription) {
- this.cancelSubscription();
- }
- }
-
- render(): JSX.Element {
- console.log("rendering nav bar");
- return (
- <div className="nav" id="header">
- <Tab target="/balance">{i18n.str`Balance`}</Tab>
- <Tab target="/history">{i18n.str`History`}</Tab>
- <Tab target="/settings">{i18n.str`Settings`}</Tab>
- <Tab target="/debug">{i18n.str`Debug`}</Tab>
- </div>
- );
- }
-}
-
-/**
- * Render an amount as a large number with a small currency symbol.
- */
-function bigAmount(amount: AmountJson): JSX.Element {
- const v = amount.value + amount.fraction / Amounts.fractionalBase;
- return (
- <span>
- <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "}
- <span>{amount.currency}</span>
- </span>
- );
-}
-
-function EmptyBalanceView(): JSX.Element {
- return (
- <i18n.Translate wrap="p">
- You have no balance to show. Need some{" "}
- <PageLink pageName="welcome.html">help</PageLink> getting started?
- </i18n.Translate>
- );
-}
-
-class WalletBalanceView extends React.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.isNonZero(pendingIncoming)) {
- incoming = (
- <i18n.Translate wrap="span">
- <span style={{ color: "darkgreen" }}>
- {"+"}
- {renderAmount(entry.pendingIncoming)}
- </span>{" "}
- incoming
- </i18n.Translate>
- );
- }
-
- 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 />
- );
- }
-}
-
-function Icon({ l }: { l: string }): JSX.Element {
- return <div className={"icon"}>{l}</div>;
-}
-
-function formatAndCapitalize(text: string): string {
- text = text.replace("-", " ");
- text = text.replace(/^./, text[0].toUpperCase());
- return text;
-}
-
-const HistoryComponent = (props: any): JSX.Element => {
- return <span>TBD</span>;
-};
-
-class WalletSettings extends React.Component<any, any> {
- render(): JSX.Element {
- return (
- <div>
- <h2>Permissions</h2>
- <PermissionsCheckbox />
- </div>
- );
- }
-}
-
-function reload(): void {
- try {
- chrome.runtime.reload();
- window.close();
- } catch (e) {
- // Functionality missing in firefox, ignore!
- }
-}
-
-function confirmReset(): void {
- if (
- confirm(
- "Do you want to IRREVOCABLY DESTROY everything inside your" +
- " wallet and LOSE ALL YOUR COINS?",
- )
- ) {
- wxApi.resetDb();
- window.close();
- }
-}
-
-function WalletDebug(props: any): JSX.Element {
- return (
- <div>
- <p>Debug tools:</p>
- <button onClick={openExtensionPage("/popup.html")}>wallet tab</button>
- <button onClick={openExtensionPage("/benchmark.html")}>benchmark</button>
- <button onClick={openExtensionPage("/show-db.html")}>show db</button>
- <button onClick={openExtensionPage("/tree.html")}>show tree</button>
- <br />
- <button onClick={confirmReset}>reset</button>
- <button onClick={reload}>reload chrome extension</button>
- </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;
-}
-
-function actionForTalerUri(talerUri: string): string | undefined {
- const uriType = classifyTalerUri(talerUri);
- switch (uriType) {
- case TalerUriType.TalerWithdraw:
- return makeExtensionUrlWithParams("withdraw.html", {
- talerWithdrawUri: talerUri,
- });
- case TalerUriType.TalerPay:
- return makeExtensionUrlWithParams("pay.html", {
- talerPayUri: talerUri,
- });
- case TalerUriType.TalerTip:
- return makeExtensionUrlWithParams("tip.html", {
- talerTipUri: talerUri,
- });
- case TalerUriType.TalerRefund:
- return makeExtensionUrlWithParams("refund.html", {
- 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;
-}
-
-async function findTalerUriInActiveTab(): Promise<string | undefined> {
- return new Promise((resolve, reject) => {
- chrome.tabs.executeScript(
- {
- code: `
- (() => {
- let x = document.querySelector("a[href^='taler://'");
- 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]);
- },
- );
- });
-}
-
-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" }}>
- <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>
- <WalletNavBar />
- <div style={{ margin: "1em" }}>
- <Router>
- <WalletBalanceView route="/balance" default />
- <WalletSettings route="/settings" />
- <WalletDebug route="/debug" />
- </Router>
- </div>
- </div>
- );
-}
-
-export function createPopup(): JSX.Element {
- return <WalletPopup />;
-}
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
deleted file mode 100644
index c5d6a00df..000000000
--- a/src/webex/pages/refund.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015-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/>
- */
-
-/**
- * Page that shows refund status for purchases.
- *
- * @author Florian Dold
- */
-
-import React, { useEffect, useState } from "react";
-
-import * as wxApi from "../wxApi";
-import { PurchaseDetails } from "../../types/walletTypes";
-import { AmountView } from "../renderHtml";
-
-function RefundStatusView(props: { talerRefundUri: string }): JSX.Element {
- const [applied, setApplied] = useState(false);
- const [purchaseDetails, setPurchaseDetails] = useState<
- PurchaseDetails | undefined
- >(undefined);
- const [errMsg, setErrMsg] = useState<string | undefined>(undefined);
-
- useEffect(() => {
- const doFetch = async (): Promise<void> => {
- try {
- const result = await wxApi.applyRefund(props.talerRefundUri);
- setApplied(true);
- const r = await wxApi.getPurchaseDetails(result.proposalId);
- setPurchaseDetails(r);
- } catch (e) {
- console.error(e);
- setErrMsg(e.message);
- console.log("err message", e.message);
- }
- };
- doFetch();
- }, [props.talerRefundUri]);
-
- console.log("rendering");
-
- if (errMsg) {
- return <span>Error: {errMsg}</span>;
- }
-
- if (!applied || !purchaseDetails) {
- return <span>Updating refund status</span>;
- }
-
- return (
- <>
- <h2>Refund Status</h2>
- <p>
- The product <em>{purchaseDetails.contractTerms.summary}</em> has
- received a total refund of{" "}
- <AmountView amount={purchaseDetails.totalRefundAmount} />.
- </p>
- <p>Note that additional fees from the exchange may apply.</p>
- </>
- );
-}
-
-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 requred");
- }
-
- return <RefundStatusView talerRefundUri={talerRefundUri} />;
-}
diff --git a/src/webex/pages/reset-required.tsx b/src/webex/pages/reset-required.tsx
deleted file mode 100644
index 9e40e7981..000000000
--- a/src/webex/pages/reset-required.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 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/>
- */
-
-/**
- * Page to inform the user when a database reset is required.
- *
- * @author Florian Dold
- */
-
-import * as React from "react";
-
-import * as wxApi from "../wxApi";
-
-class State {
- /**
- * Did the user check the confirmation check box?
- */
- checked: boolean;
-
- /**
- * Do we actually need to reset the db?
- */
- resetRequired: boolean;
-}
-
-class ResetNotification extends React.Component<any, State> {
- constructor(props: any) {
- super(props);
- this.state = { checked: false, resetRequired: true };
- setInterval(() => this.update(), 500);
- }
- async update(): Promise<void> {
- const res = await wxApi.checkUpgrade();
- this.setState({ resetRequired: res.dbResetRequired });
- }
- render(): JSX.Element {
- if (this.state.resetRequired) {
- return (
- <div>
- <h1>Manual Reset Reqired</h1>
- <p>
- The wallet&apos;s database in your browser is incompatible with the{" "}
- currently installed wallet. Please reset manually.
- </p>
- <p>
- Once the database format has stabilized, we will provide automatic
- upgrades.
- </p>
- <input
- id="check"
- type="checkbox"
- checked={this.state.checked}
- onChange={(e) => this.setState({ checked: e.target.checked })}
- />{" "}
- <label htmlFor="check">
- I understand that I will lose all my data
- </label>
- <br />
- <button
- className="pure-button"
- disabled={!this.state.checked}
- onClick={() => wxApi.resetDb()}
- >
- Reset
- </button>
- </div>
- );
- }
- return (
- <div>
- <h1>Everything is fine!</h1>A reset is not required anymore, you can
- close this page.
- </div>
- );
- }
-}
-
-export function createResetRequiredPage(): JSX.Element {
- return <ResetNotification />;
-}
diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx
deleted file mode 100644
index e8cf8c9dd..000000000
--- a/src/webex/pages/return-coins.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Inria
-
- 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/>
- */
-
-/**
- * Return coins to own bank account.
- *
- * @author Florian Dold
- */
-
-/**
- * Imports.
- */
-import * as React from "react";
-
-export function createReturnCoinsPage(): JSX.Element {
- return <span>Not implemented yet.</span>;
-}
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
deleted file mode 100644
index 4a1d3743a..000000000
--- a/src/webex/pages/tip.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 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/>
- */
-
-/**
- * Page shown to the user to confirm creation
- * of a reserve, usually requested by the bank.
- *
- * @author Florian Dold
- */
-
-import * as React from "react";
-
-import { acceptTip, getTipStatus } from "../wxApi";
-
-import { renderAmount, ProgressButton } from "../renderHtml";
-
-import { useState, useEffect } from "react";
-import { TipStatus } from "../../types/walletTypes";
-
-function TipDisplay(props: { talerTipUri: string }): JSX.Element {
- const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined);
- const [discarded, setDiscarded] = useState(false);
- const [loading, setLoading] = useState(false);
- const [finished, setFinished] = useState(false);
-
- useEffect(() => {
- const doFetch = async (): Promise<void> => {
- const ts = await getTipStatus(props.talerTipUri);
- setTipStatus(ts);
- };
- doFetch();
- }, [props.talerTipUri]);
-
- if (discarded) {
- return <span>You&apos;ve discarded the tip.</span>;
- }
-
- if (finished) {
- return <span>Tip has been accepted!</span>;
- }
-
- if (!tipStatus) {
- return <span>Loading ...</span>;
- }
-
- const discard = (): void => {
- setDiscarded(true);
- };
-
- const accept = async (): Promise<void> => {
- setLoading(true);
- await acceptTip(tipStatus.tipId);
- setFinished(true);
- };
-
- return (
- <div>
- <h2>Tip Received!</h2>
- <p>
- You received a tip of <strong>{renderAmount(tipStatus.amount)}</strong>{" "}
- from <span> </span>
- <strong>{tipStatus.merchantOrigin}</strong>.
- </p>
- <p>
- The tip is handled by the exchange{" "}
- <strong>{tipStatus.exchangeUrl}</strong>. This exchange will charge fees
- of <strong>{renderAmount(tipStatus.totalFees)}</strong> for this
- operation.
- </p>
- <form className="pure-form">
- <ProgressButton loading={loading} onClick={() => accept()}>
- Accept Tip
- </ProgressButton>{" "}
- <button className="pure-button" type="button" onClick={() => discard()}>
- Discard tip
- </button>
- </form>
- </div>
- );
-}
-
-export function createTipPage(): JSX.Element {
- const url = new URL(document.location.href);
- const talerTipUri = url.searchParams.get("talerTipUri");
- if (typeof talerTipUri !== "string") {
- throw Error("talerTipUri must be a string");
- }
-
- return <TipDisplay talerTipUri={talerTipUri} />;
-}
diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx
deleted file mode 100644
index a7c24d659..000000000
--- a/src/webex/pages/welcome.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems SA
-
- 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/>
- */
-
-/**
- * Welcome page, shown on first installs.
- *
- * @author Florian Dold
- */
-
-import React, { useState, useEffect } from "react";
-import { getDiagnostics } from "../wxApi";
-import { PageLink } from "../renderHtml";
-import { WalletDiagnostics } from "../../types/walletTypes";
-import * as wxApi from "../wxApi";
-import { getPermissionsApi } from "../compat";
-import { extendedPermissions } from "../permissions";
-
-function Diagnostics(): JSX.Element | null {
- const [timedOut, setTimedOut] = useState(false);
- const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
- undefined,
- );
-
- useEffect(() => {
- let gotDiagnostics = false;
- setTimeout(() => {
- if (!gotDiagnostics) {
- console.error("timed out");
- setTimedOut(true);
- }
- }, 1000);
- const doFetch = async (): Promise<void> => {
- const d = await getDiagnostics();
- console.log("got diagnostics", d);
- gotDiagnostics = true;
- setDiagnostics(d);
- };
- console.log("fetching diagnostics");
- doFetch();
- }, []);
-
- if (timedOut) {
- return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>;
- }
-
- if (diagnostics) {
- if (diagnostics.errors.length === 0) {
- return null;
- } else {
- return (
- <div
- style={{
- borderLeft: "0.5em solid red",
- paddingLeft: "1em",
- paddingTop: "0.2em",
- paddingBottom: "0.2em",
- }}
- >
- <p>Problems detected:</p>
- <ol>
- {diagnostics.errors.map((errMsg) => (
- <li key={errMsg}>{errMsg}</li>
- ))}
- </ol>
- {diagnostics.firefoxIdbProblem ? (
- <p>
- Please check in your <code>about:config</code> settings that you
- have IndexedDB enabled (check the preference name{" "}
- <code>dom.indexedDB.enabled</code>).
- </p>
- ) : null}
- {diagnostics.dbOutdated ? (
- <p>
- Your wallet database is outdated. Currently automatic migration is
- not supported. Please go{" "}
- <PageLink pageName="reset-required.html">here</PageLink> to reset
- the wallet database.
- </p>
- ) : null}
- </div>
- );
- }
- }
-
- return <p>Running diagnostics ...</p>;
-}
-
-export function PermissionsCheckbox(): JSX.Element {
- const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState(
- false,
- );
- async function handleExtendedPerm(requestedVal: boolean): Promise<void> {
- let nextVal: boolean | undefined;
- if (requestedVal) {
- 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);
- console.log(res);
- nextVal = res.newValue;
- } else {
- const res = await wxApi.setExtendedPermissions(false);
- console.log(res);
- nextVal = res.newValue;
- }
- console.log("new permissions applied:", nextVal);
- setExtendedPermissionsEnabled(nextVal ?? false);
- }
- useEffect(() => {
- async function getExtendedPermValue(): Promise<void> {
- const res = await wxApi.getExtendedPermissions();
- setExtendedPermissionsEnabled(res.newValue);
- }
- getExtendedPermValue();
- });
- return (
- <div>
- <input
- checked={extendedPermissionsEnabled}
- onChange={(x) => handleExtendedPerm(x.target.checked)}
- 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 Welcome(): JSX.Element {
- return (
- <>
- <p>Thank you for installing the wallet.</p>
- <Diagnostics />
- <h2>Permissions</h2>
- <PermissionsCheckbox />
- <h2>Next Steps</h2>
- <a href="https://demo.taler.net/" style={{ display: "block" }}>
- Try the demo »
- </a>
- <a href="https://demo.taler.net/" style={{ display: "block" }}>
- Learn how to top up your wallet balance »
- </a>
- </>
- );
-}
-
-export function createWelcomePage(): JSX.Element {
- return <Welcome />;
-}
diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx
deleted file mode 100644
index 4a92704b3..000000000
--- a/src/webex/pages/withdraw.tsx
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015-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/>
- */
-
-/**
- * Page shown to the user to confirm creation
- * of a reserve, usually requested by the bank.
- *
- * @author Florian Dold
- */
-
-import * as i18n from "../i18n";
-
-import { WithdrawDetailView, renderAmount } from "../renderHtml";
-
-import React, { useState, useEffect } from "react";
-import {
- acceptWithdrawal,
- onUpdateNotification,
-} from "../wxApi";
-
-function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
- const [details, setDetails] = useState<
- any | undefined
- >();
- const [selectedExchange, setSelectedExchange] = useState<
- string | undefined
- >();
- const talerWithdrawUri = props.talerWithdrawUri;
- const [cancelled, setCancelled] = useState(false);
- const [selecting, setSelecting] = useState(false);
- const [customUrl, setCustomUrl] = useState<string>("");
- const [errMsg, setErrMsg] = useState<string | undefined>("");
- const [updateCounter, setUpdateCounter] = useState(1);
-
- useEffect(() => {
- return onUpdateNotification(() => {
- setUpdateCounter(updateCounter + 1);
- });
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- useEffect(() => {
- const fetchData = async (): Promise<void> => {
- // FIXME: re-implement with new API
- // console.log("getting from", talerWithdrawUri);
- // let d: WithdrawalDetailsResponse | undefined = undefined;
- // try {
- // d = await getWithdrawDetails(talerWithdrawUri, selectedExchange);
- // } catch (e) {
- // console.error(
- // `error getting withdraw details for uri ${talerWithdrawUri}, exchange ${selectedExchange}`,
- // e,
- // );
- // setErrMsg(e.message);
- // return;
- // }
- // console.log("got withdrawDetails", d);
- // if (!selectedExchange && d.bankWithdrawDetails.suggestedExchange) {
- // console.log("setting selected exchange");
- // setSelectedExchange(d.bankWithdrawDetails.suggestedExchange);
- // }
- // setDetails(d);
- };
- fetchData();
- }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]);
-
- if (errMsg) {
- return (
- <div>
- <i18n.Translate wrap="p">
- Could not get details for withdraw operation:
- </i18n.Translate>
- <p style={{ color: "red" }}>{errMsg}</p>
- <p>
- <span
- role="button"
- tabIndex={0}
- style={{ textDecoration: "underline", cursor: "pointer" }}
- onClick={() => {
- setSelecting(true);
- setErrMsg(undefined);
- setSelectedExchange(undefined);
- setDetails(undefined);
- }}
- >
- {i18n.str`Chose different exchange provider`}
- </span>
- </p>
- </div>
- );
- }
-
- if (!details) {
- return <span>Loading...</span>;
- }
-
- if (cancelled) {
- return <span>Withdraw operation has been cancelled.</span>;
- }
-
- if (selecting) {
- const bankSuggestion =
- details && details.bankWithdrawDetails.suggestedExchange;
- return (
- <div>
- {i18n.str`Please select an exchange. You can review the details before after your selection.`}
- {bankSuggestion && (
- <div>
- <h2>Bank Suggestion</h2>
- <button
- className="pure-button button-success"
- onClick={() => {
- setDetails(undefined);
- setSelectedExchange(bankSuggestion);
- setSelecting(false);
- }}
- >
- <i18n.Translate wrap="span">
- Select <strong>{bankSuggestion}</strong>
- </i18n.Translate>
- </button>
- </div>
- )}
- <h2>Custom Selection</h2>
- <p>
- <input
- type="text"
- onChange={(e) => setCustomUrl(e.target.value)}
- value={customUrl}
- />
- </p>
- <button
- className="pure-button button-success"
- onClick={() => {
- setDetails(undefined);
- setSelectedExchange(customUrl);
- setSelecting(false);
- }}
- >
- <i18n.Translate wrap="span">Select custom exchange</i18n.Translate>
- </button>
- </div>
- );
- }
-
- const accept = async (): Promise<void> => {
- if (!selectedExchange) {
- throw Error("can't accept, no exchange selected");
- }
- console.log("accepting exchange", selectedExchange);
- const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange);
- console.log("accept withdrawal response", res);
- if (res.confirmTransferUrl) {
- document.location.href = res.confirmTransferUrl;
- }
- };
-
- return (
- <div>
- <h1>Digital Cash Withdrawal</h1>
- <i18n.Translate wrap="p">
- You are about to withdraw{" "}
- <strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from
- your bank account into your wallet.
- </i18n.Translate>
- {selectedExchange ? (
- <p>
- The exchange <strong>{selectedExchange}</strong> will be used as the
- Taler payment service provider.
- </p>
- ) : null}
-
- <div>
- <button
- className="pure-button button-success"
- disabled={!selectedExchange}
- onClick={() => accept()}
- >
- {i18n.str`Accept fees and withdraw`}
- </button>
- <p>
- <span
- role="button"
- tabIndex={0}
- style={{ textDecoration: "underline", cursor: "pointer" }}
- onClick={() => setSelecting(true)}
- >
- {i18n.str`Chose different exchange provider`}
- </span>
- <br />
- <span
- role="button"
- tabIndex={0}
- style={{ textDecoration: "underline", cursor: "pointer" }}
- onClick={() => setCancelled(true)}
- >
- {i18n.str`Cancel withdraw operation`}
- </span>
- </p>
-
- {details.exchangeWithdrawDetails ? (
- <WithdrawDetailView rci={details.exchangeWithdrawDetails} />
- ) : null}
- </div>
- </div>
- );
-}
-
-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} />;
-}