aboutsummaryrefslogtreecommitdiff
path: root/src/pages
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-05-28 23:15:41 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-05-28 23:15:41 +0200
commitb6e774585d32017e5f1ceeeb2b2e2a5e350354d3 (patch)
tree080cb5afe3b48c0428abd2d7de1ff7fe34d9b9b1 /src/pages
parent38a74188d759444d7e1abac856f78ae710e2a4c5 (diff)
move webex specific things in their own directory
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/add-auditor.html34
-rw-r--r--src/pages/add-auditor.tsx125
-rw-r--r--src/pages/auditors.html36
-rw-r--r--src/pages/auditors.tsx146
-rw-r--r--src/pages/confirm-contract.html69
-rw-r--r--src/pages/confirm-contract.tsx240
-rw-r--r--src/pages/confirm-create-reserve.html52
-rw-r--r--src/pages/confirm-create-reserve.tsx639
-rw-r--r--src/pages/error.html18
-rw-r--r--src/pages/error.tsx63
-rw-r--r--src/pages/help/empty-wallet.html30
-rw-r--r--src/pages/logs.html27
-rw-r--r--src/pages/logs.tsx82
-rw-r--r--src/pages/payback.html36
-rw-r--r--src/pages/payback.tsx98
-rw-r--r--src/pages/popup.css84
-rw-r--r--src/pages/popup.html18
-rw-r--r--src/pages/popup.tsx545
-rw-r--r--src/pages/show-db.html18
-rw-r--r--src/pages/show-db.ts94
-rw-r--r--src/pages/tree.html27
-rw-r--r--src/pages/tree.tsx436
22 files changed, 0 insertions, 2917 deletions
diff --git a/src/pages/add-auditor.html b/src/pages/add-auditor.html
deleted file mode 100644
index b7a9d041d..000000000
--- a/src/pages/add-auditor.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
-
- <title>Taler Wallet: Add Auditor</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/add-auditor-bundle.js"></script>
-
- <style>
- .tree-item {
- margin: 2em;
- border-radius: 5px;
- border: 1px solid gray;
- padding: 1em;
- }
- .button-linky {
- background: none;
- color: black;
- text-decoration: underline;
- border: none;
- }
- </style>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/add-auditor.tsx b/src/pages/add-auditor.tsx
deleted file mode 100644
index 8bef557d9..000000000
--- a/src/pages/add-auditor.tsx
+++ /dev/null
@@ -1,125 +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 {
- ExchangeRecord,
- DenominationRecord,
- AuditorRecord,
- CurrencyRecord,
- ReserveRecord,
- CoinRecord,
- PreCoinRecord,
- Denomination
-} from "../types";
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
- getCurrencies,
- updateCurrency,
-} from "../wxApi";
-import { getTalerStampDate } from "../helpers";
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-
-interface ConfirmAuditorProps {
- url: string;
- currency: string;
- auditorPub: string;
- expirationStamp: number;
-}
-
-class ConfirmAuditor extends ImplicitStateComponent<ConfirmAuditorProps> {
- addDone: StateHolder<boolean> = this.makeState(false);
- constructor() {
- super();
- }
-
- async add() {
- let currencies = await getCurrencies();
- let currency: CurrencyRecord|undefined = undefined;
-
- for (let c of currencies) {
- if (c.name == this.props.currency) {
- currency = c;
- }
- }
-
- if (!currency) {
- currency = { name: this.props.currency, auditors: [], fractionalDigits: 2, exchanges: [] };
- }
-
- let newAuditor = { auditorPub: this.props.auditorPub, baseUrl: this.props.url, expirationStamp: this.props.expirationStamp };
-
- let auditorFound = false;
- for (let idx in currency.auditors) {
- let a = currency.auditors[idx];
- if (a.baseUrl == this.props.url) {
- auditorFound = true;
- // Update auditor if already found by URL.
- currency.auditors[idx] = newAuditor;
- }
- }
-
- if (!auditorFound) {
- currency.auditors.push(newAuditor);
- }
-
- await updateCurrency(currency);
-
- this.addDone(true);
- }
-
- back() {
- window.history.back();
- }
-
- render(): JSX.Element {
- return (
- <div id="main">
- <p>Do you want to let <strong>{this.props.auditorPub}</strong> audit the currency "{this.props.currency}"?</p>
- {this.addDone() ?
- (<div>Auditor was added! You can also <a href={chrome.extension.getURL("/src/pages/auditors.html")}>view and edit</a> auditors.</div>)
- :
- (<div>
- <button onClick={() => this.add()} className="pure-button pure-button-primary">Yes</button>
- <button onClick={() => this.back()} className="pure-button">No</button>
- </div>)
- }
- </div>
- );
- }
-}
-
-export function main() {
- const walletPageUrl = new URI(document.location.href);
- const query: any = JSON.parse((URI.parseQuery(walletPageUrl.query()) as any)["req"]);
- const url = query.url;
- const currency: string = query.currency;
- const auditorPub: string = query.auditorPub;
- const expirationStamp = Number.parseInt(query.expirationStamp);
- const args = { url, currency, auditorPub, expirationStamp };
- ReactDOM.render(<ConfirmAuditor {...args} />, document.getElementById("container")!);
-}
-
-document.addEventListener("DOMContentLoaded", main);
diff --git a/src/pages/auditors.html b/src/pages/auditors.html
deleted file mode 100644
index cbfc3b4b5..000000000
--- a/src/pages/auditors.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Auditors</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/auditors-bundle.js"></script>
-
- <style>
- body {
- font-size: 100%;
- }
- .tree-item {
- margin: 2em;
- border-radius: 5px;
- border: 1px solid gray;
- padding: 1em;
- }
- .button-linky {
- background: none;
- color: black;
- text-decoration: underline;
- border: none;
- }
- </style>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/auditors.tsx b/src/pages/auditors.tsx
deleted file mode 100644
index f263d2ec9..000000000
--- a/src/pages/auditors.tsx
+++ /dev/null
@@ -1,146 +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 {
- ExchangeRecord,
- ExchangeForCurrencyRecord,
- DenominationRecord,
- AuditorRecord,
- CurrencyRecord,
- ReserveRecord,
- CoinRecord,
- PreCoinRecord,
- Denomination
-} from "../types";
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
- getCurrencies,
- updateCurrency,
-} from "../wxApi";
-import { getTalerStampDate } from "../helpers";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-
-interface CurrencyListState {
- currencies?: CurrencyRecord[];
-}
-
-class CurrencyList extends React.Component<any, CurrencyListState> {
- constructor() {
- super();
- let 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() {
- let currencies = await getCurrencies();
- console.log("currencies: ", currencies);
- this.setState({ currencies });
- }
-
- async confirmRemoveAuditor(c: CurrencyRecord, a: AuditorRecord) {
- 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) {
- 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>{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>{e.baseUrl} <button className="pure-button button-destructive" onClick={() => this.confirmRemoveExchange(c, e)}>Remove</button>
- </li>
- ))}
- </ul>
- </div>
- );
- }
-
- render(): JSX.Element {
- let currencies = this.state.currencies;
- if (!currencies) {
- return <span>...</span>;
- }
- return (
- <div id="main">
- {currencies.map(c => (
- <div>
- <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 main() {
- ReactDOM.render(<CurrencyList />, document.getElementById("container")!);
-}
-
-document.addEventListener("DOMContentLoaded", main);
diff --git a/src/pages/confirm-contract.html b/src/pages/confirm-contract.html
deleted file mode 100644
index 6713b2e2c..000000000
--- a/src/pages/confirm-contract.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Confirm Reserve Creation</title>
-
- <link rel="stylesheet" type="text/css" href="/src/style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/confirm-contract-bundle.js"></script>
-
- <style>
- button.accept {
- background-color: #5757D2;
- border: 1px solid black;
- border-radius: 5px;
- margin: 1em 0;
- padding: 0.5em;
- font-weight: bold;
- color: white;
- }
- button.linky {
- background:none!important;
- border:none;
- padding:0!important;
-
- font-family:arial,sans-serif;
- color:#069;
- text-decoration:underline;
- cursor:pointer;
- }
-
- input.url {
- width: 25em;
- }
-
-
- button.accept:disabled {
- background-color: #dedbe8;
- border: 1px solid white;
- border-radius: 5px;
- margin: 1em 0;
- padding: 0.5em;
- font-weight: bold;
- color: #2C2C2C;
- }
-
- .errorbox {
- border: 1px solid;
- display: inline-block;
- margin: 1em;
- padding: 1em;
- font-weight: bold;
- background: #FF8A8A;
- }
- </style>
-</head>
-
-<body>
- <section id="main">
- <h1>GNU Taler Wallet</h1>
- <article id="contract" class="fade"></article>
- </section>
-</body>
-
-</html>
diff --git a/src/pages/confirm-contract.tsx b/src/pages/confirm-contract.tsx
deleted file mode 100644
index 47db94ee8..000000000
--- a/src/pages/confirm-contract.tsx
+++ /dev/null
@@ -1,240 +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 { Contract, AmountJson, ExchangeRecord } from "../types";
-import { OfferRecord } from "../wallet";
-import { renderContract } from "../renderHtml";
-import { getExchanges } from "../wxApi";
-import * as i18n from "../i18n";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-
-
-interface DetailState {
- collapsed: boolean;
-}
-
-interface DetailProps {
- contract: Contract
- collapsed: boolean
- exchanges: null|ExchangeRecord[];
-}
-
-
-class Details extends React.Component<DetailProps, DetailState> {
- constructor(props: DetailProps) {
- super(props);
- console.log("new Details component created");
- this.state = {
- collapsed: props.collapsed,
- };
-
- console.log("initial state:", this.state);
- }
-
- render() {
- if (this.state.collapsed) {
- return (
- <div>
- <button className="linky"
- onClick={() => { this.setState({collapsed: false} as any)}}>
- <i18n.Translate wrap="span">
- show more details
- </i18n.Translate>
- </button>
- </div>
- );
- } else {
- return (
- <div>
- <button className="linky"
- onClick={() => this.setState({collapsed: true} as any)}>
- show less details
- </button>
- <div>
- {i18n.str`Accepted exchanges:`}
- <ul>
- {this.props.contract.exchanges.map(
- e => <li>{`${e.url}: ${e.master_pub}`}</li>)}
- </ul>
- {i18n.str`Exchanges in the wallet:`}
- <ul>
- {(this.props.exchanges || []).map(
- (e: ExchangeRecord) =>
- <li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)}
- </ul>
- </div>
- </div>);
- }
- }
-}
-
-interface ContractPromptProps {
- offerId: number;
-}
-
-interface ContractPromptState {
- offer: OfferRecord|null;
- error: string|null;
- payDisabled: boolean;
- exchanges: null|ExchangeRecord[];
-}
-
-class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
- constructor() {
- super();
- this.state = {
- offer: null,
- error: null,
- payDisabled: true,
- exchanges: null
- }
- }
-
- componentWillMount() {
- this.update();
- }
-
- componentWillUnmount() {
- // FIXME: abort running ops
- }
-
- async update() {
- let offer = await this.getOffer();
- this.setState({offer} as any);
- this.checkPayment();
- let exchanges = await getExchanges();
- this.setState({exchanges} as any);
- }
-
- getOffer(): Promise<OfferRecord> {
- return new Promise<OfferRecord>((resolve, reject) => {
- let msg = {
- type: 'get-offer',
- detail: {
- offerId: this.props.offerId
- }
- };
- chrome.runtime.sendMessage(msg, (resp) => {
- resolve(resp);
- });
- })
- }
-
- checkPayment() {
- let msg = {
- type: 'check-pay',
- detail: {
- offer: this.state.offer
- }
- };
- chrome.runtime.sendMessage(msg, (resp) => {
- if (resp.error) {
- console.log("check-pay error", JSON.stringify(resp));
- switch (resp.error) {
- case "coins-insufficient":
- let msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`;
- let msgNoMatch = i18n.str`You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.`;
- if (this.state.exchanges && this.state.offer) {
- let acceptedExchangePubs = this.state.offer.contract.exchanges.map((e) => e.master_pub);
- let ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0);
- if (ex) {
- this.setState({error: msgInsufficient});
- } else {
- this.setState({error: msgNoMatch});
- }
- } else {
- this.setState({error: msgInsufficient});
- }
- break;
- default:
- this.setState({error: `Error: ${resp.error}`});
- break;
- }
- this.setState({payDisabled: true});
- } else {
- this.setState({payDisabled: false, error: null});
- }
- this.setState({} as any);
- window.setTimeout(() => this.checkPayment(), 500);
- });
- }
-
- doPayment() {
- let d = {offer: this.state.offer};
- chrome.runtime.sendMessage({type: 'confirm-pay', detail: d}, (resp) => {
- if (resp.error) {
- console.log("confirm-pay error", JSON.stringify(resp));
- switch (resp.error) {
- case "coins-insufficient":
- this.setState({error: "You do not have enough coins of the requested currency."});
- break;
- default:
- this.setState({error: `Error: ${resp.error}`});
- break;
- }
- return;
- }
- let c = d.offer!.contract;
- console.log("contract", c);
- document.location.href = c.fulfillment_url;
- });
- }
-
-
- render() {
- if (!this.state.offer) {
- return <span>...</span>;
- }
- let c = this.state.offer.contract;
- return (
- <div>
- <div>
- {renderContract(c)}
- </div>
- <button onClick={() => this.doPayment()}
- disabled={this.state.payDisabled}
- className="accept">
- Confirm payment
- </button>
- <div>
- {(this.state.error ? <p className="errorbox">{this.state.error}</p> : <p />)}
- </div>
- <Details exchanges={this.state.exchanges} contract={c} collapsed={!this.state.error}/>
- </div>
- );
- }
-}
-
-
-document.addEventListener("DOMContentLoaded", () => {
- let url = new URI(document.location.href);
- let query: any = URI.parseQuery(url.query());
- let offerId = JSON.parse(query.offerId);
-
- ReactDOM.render(<ContractPrompt offerId={offerId}/>, document.getElementById(
- "contract")!);
-});
diff --git a/src/pages/confirm-create-reserve.html b/src/pages/confirm-create-reserve.html
deleted file mode 100644
index 16ab12a30..000000000
--- a/src/pages/confirm-create-reserve.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Select Taler Provider</title>
-
- <link rel="icon" href="/img/icon.png">
- <link rel="stylesheet" type="text/css" href="/src/style/wallet.css">
- <link rel="stylesheet" type="text/css" href="/src/style/pure.css">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/confirm-create-reserve-bundle.js"></script>
-
- <style>
- body {
- font-size: 100%;
- overflow-y: scroll;
- }
- .button-success {
- background: rgb(28, 184, 65); /* this is a green */
- color: white;
- border-radius: 4px;
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- }
- .button-secondary {
- background: rgb(66, 184, 221); /* this is a light blue */
- color: white;
- border-radius: 4px;
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- }
- a.opener {
- color: black;
- }
- .opener-open::before {
- content: "\25bc"
- }
- .opener-collapsed::before {
- content: "\25b6 "
- }
- </style>
-
-</head>
-
-<body>
- <section id="main">
- <h1>GNU Taler Wallet</h1>
- <div class="fade" id="exchange-selection"></div>
- </section>
-</body>
-
-</html>
diff --git a/src/pages/confirm-create-reserve.tsx b/src/pages/confirm-create-reserve.tsx
deleted file mode 100644
index 2f341bb4e..000000000
--- a/src/pages/confirm-create-reserve.tsx
+++ /dev/null
@@ -1,639 +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 {amountToPretty, canonicalizeBaseUrl} from "../helpers";
-import {
- AmountJson, CreateReserveResponse,
- ReserveCreationInfo, Amounts,
- Denomination, DenominationRecord, CurrencyRecord
-} from "../types";
-import {getReserveCreationInfo, getCurrency, getExchangeInfo} from "../wxApi";
-import {ImplicitStateComponent, StateHolder} from "../components";
-import * as i18n from "../i18n";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-import * as moment from "moment";
-
-
-function delay<T>(delayMs: number, value: T): Promise<T> {
- return new Promise<T>((resolve, reject) => {
- setTimeout(() => resolve(value), delayMs);
- });
-}
-
-class EventTrigger {
- triggerResolve: any;
- triggerPromise: Promise<boolean>;
-
- constructor() {
- this.reset();
- }
-
- private reset() {
- this.triggerPromise = new Promise<boolean>((resolve, reject) => {
- this.triggerResolve = resolve;
- });
- }
-
- trigger() {
- this.triggerResolve(false);
- this.reset();
- }
-
- async wait(delayMs: number): Promise<boolean> {
- return await Promise.race([this.triggerPromise, delay(delayMs, true)]);
- }
-}
-
-
-interface CollapsibleState {
- collapsed: boolean;
-}
-
-interface CollapsibleProps {
- initiallyCollapsed: boolean;
- title: string;
-}
-
-class Collapsible extends React.Component<CollapsibleProps, CollapsibleState> {
- constructor(props: CollapsibleProps) {
- super(props);
- this.state = { collapsed: props.initiallyCollapsed };
- }
- render() {
- const doOpen = (e: any) => {
- this.setState({collapsed: false})
- e.preventDefault()
- };
- const doClose = (e: any) => {
- this.setState({collapsed: true})
- e.preventDefault();
- };
- if (this.state.collapsed) {
- return <h2><a className="opener opener-collapsed" href="#" onClick={doOpen}>{this.props.title}</a></h2>;
- }
- return (
- <div>
- <h2><a className="opener opener-open" href="#" onClick={doClose}>{this.props.title}</a></h2>
- {this.props.children}
- </div>
- );
- }
-}
-
-function renderAuditorDetails(rci: ReserveCreationInfo|null) {
- if (!rci) {
- return (
- <p>
- Details will be displayed when a valid exchange provider URL is entered.
- </p>
- );
- }
- if (rci.exchangeInfo.auditors.length == 0) {
- return (
- <p>
- The exchange is not audited by any auditors.
- </p>
- );
- }
- return (
- <div>
- {rci.exchangeInfo.auditors.map(a => (
- <h3>Auditor {a.url}</h3>
- ))}
- </div>
- );
-}
-
-function renderReserveCreationDetails(rci: ReserveCreationInfo|null) {
- if (!rci) {
- return (
- <p>
- Details will be displayed when a valid exchange provider URL is entered.
- </p>
- );
- }
-
- let denoms = rci.selectedDenoms;
-
- let countByPub: {[s: string]: number} = {};
- let uniq: DenominationRecord[] = [];
-
- denoms.forEach((x: DenominationRecord) => {
- let c = countByPub[x.denomPub] || 0;
- if (c == 0) {
- uniq.push(x);
- }
- c += 1;
- countByPub[x.denomPub] = c;
- });
-
- function row(denom: DenominationRecord) {
- return (
- <tr>
- <td>{countByPub[denom.denomPub] + "x"}</td>
- <td>{amountToPretty(denom.value)}</td>
- <td>{amountToPretty(denom.feeWithdraw)}</td>
- <td>{amountToPretty(denom.feeRefresh)}</td>
- <td>{amountToPretty(denom.feeDeposit)}</td>
- </tr>
- );
- }
-
- function wireFee(s: string) {
- return [
- <thead>
- <tr>
- <th colSpan={3}>Wire Method {s}</th>
- </tr>
- <tr>
- <th>Applies Until</th>
- <th>Wire Fee</th>
- <th>Closing Fee</th>
- </tr>
- </thead>,
- <tbody>
- {rci!.wireFees.feesForType[s].map(f => (
- <tr>
- <td>{moment.unix(f.endStamp).format("llll")}</td>
- <td>{amountToPretty(f.wireFee)}</td>
- <td>{amountToPretty(f.closingFee)}</td>
- </tr>
- ))}
- </tbody>
- ];
- }
-
- let withdrawFeeStr = amountToPretty(rci.withdrawFee);
- let overheadStr = amountToPretty(rci.overhead);
-
- return (
- <div>
- <h3>Overview</h3>
- <p>{i18n.str`Withdrawal fees: ${withdrawFeeStr}`}</p>
- <p>{i18n.str`Rounding loss: ${overheadStr}`}</p>
- <p>{i18n.str`Earliest expiration (for deposit): ${moment.unix(rci.earliestDepositExpiration).fromNow()}`}</p>
- <h3>Coin Fees</h3>
- <table className="pure-table">
- <thead>
- <tr>
- <th>{i18n.str`# Coins`}</th>
- <th>{i18n.str`Value`}</th>
- <th>{i18n.str`Withdraw Fee`}</th>
- <th>{i18n.str`Refresh Fee`}</th>
- <th>{i18n.str`Deposit Fee`}</th>
- </tr>
- </thead>
- <tbody>
- {uniq.map(row)}
- </tbody>
- </table>
- <h3>Wire Fees</h3>
- <table className="pure-table">
- {Object.keys(rci.wireFees.feesForType).map(wireFee)}
- </table>
- </div>
- );
-}
-
-
-function getSuggestedExchange(currency: string): Promise<string> {
- // TODO: make this request go to the wallet backend
- // Right now, this is a stub.
- const defaultExchange: {[s: string]: string} = {
- "KUDOS": "https://exchange.demo.taler.net",
- "PUDOS": "https://exchange.test.taler.net",
- };
-
- let exchange = defaultExchange[currency];
-
- if (!exchange) {
- exchange = ""
- }
-
- return Promise.resolve(exchange);
-}
-
-
-function WithdrawFee(props: {reserveCreationInfo: ReserveCreationInfo|null}): JSX.Element {
- if (props.reserveCreationInfo) {
- let {overhead, withdrawFee} = props.reserveCreationInfo;
- let totalCost = Amounts.add(overhead, withdrawFee).amount;
- return <p>{i18n.str`Withdraw fees:`} {amountToPretty(totalCost)}</p>;
- }
- return <p />;
-}
-
-
-interface ExchangeSelectionProps {
- suggestedExchangeUrl: string;
- amount: AmountJson;
- callback_url: string;
- wt_types: string[];
- currencyRecord: CurrencyRecord|null;
-}
-
-interface ManualSelectionProps {
- onSelect(url: string): void;
- initialUrl: string;
-}
-
-class ManualSelection extends ImplicitStateComponent<ManualSelectionProps> {
- url: StateHolder<string> = this.makeState("");
- errorMessage: StateHolder<string|null> = this.makeState(null);
- isOkay: StateHolder<boolean> = this.makeState(false);
- updateEvent = new EventTrigger();
- constructor(p: ManualSelectionProps) {
- super(p);
- this.url(p.initialUrl);
- this.update();
- }
- render() {
- return (
- <div className="pure-g pure-form pure-form-stacked">
- <div className="pure-u-1">
- <label>URL</label>
- <input className="url" type="text" spellCheck={false}
- value={this.url()}
- key="exchange-url-input"
- onInput={(e) => this.onUrlChanged((e.target as HTMLInputElement).value)} />
- </div>
- <div className="pure-u-1">
- <button className="pure-button button-success"
- disabled={!this.isOkay()}
- onClick={() => this.props.onSelect(this.url())}>
- {i18n.str`Select`}
- </button>
- {this.errorMessage()}
- </div>
- </div>
- );
- }
-
- async update() {
- this.errorMessage(null);
- this.isOkay(false);
- if (!this.url()) {
- return;
- }
- let parsedUrl = new URI(this.url()!);
- if (parsedUrl.is("relative")) {
- this.errorMessage(i18n.str`Error: URL may not be relative`);
- this.isOkay(false);
- return;
- }
- try {
- let url = canonicalizeBaseUrl(this.url()!);
- let r = await getExchangeInfo(url)
- console.log("getExchangeInfo returned")
- this.isOkay(true);
- } catch (e) {
- console.log("got error", e);
- if (e.hasOwnProperty("httpStatus")) {
- this.errorMessage(`Error: request failed with status ${e.httpStatus}`);
- } else if (e.hasOwnProperty("errorResponse")) {
- let resp = e.errorResponse;
- this.errorMessage(`Error: ${resp.error} (${resp.hint})`);
- } else {
- this.errorMessage("invalid exchange URL");
- }
- }
- }
-
- async onUrlChanged(s: string) {
- this.url(s);
- this.errorMessage(null);
- this.isOkay(false);
- this.updateEvent.trigger();
- let waited = await this.updateEvent.wait(200);
- if (waited) {
- // Run the actual update if nobody else preempted us.
- this.update();
- }
- }
-}
-
-
-class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
- statusString: StateHolder<string|null> = this.makeState(null);
- reserveCreationInfo: StateHolder<ReserveCreationInfo|null> = this.makeState(
- null);
- url: StateHolder<string|null> = this.makeState(null);
-
- selectingExchange: StateHolder<boolean> = this.makeState(false);
-
- constructor(props: ExchangeSelectionProps) {
- super(props);
- let prefilledExchangesUrls = [];
- if (props.currencyRecord) {
- let exchanges = props.currencyRecord.exchanges.map((x) => x.baseUrl);
- prefilledExchangesUrls.push(...exchanges);
- }
- if (props.suggestedExchangeUrl) {
- prefilledExchangesUrls.push(props.suggestedExchangeUrl);
- }
- if (prefilledExchangesUrls.length != 0) {
- this.url(prefilledExchangesUrls[0]);
- this.forceReserveUpdate();
- } else {
- this.selectingExchange(true);
- }
- }
-
- renderFeeStatus() {
- let rci = this.reserveCreationInfo();
- if (rci) {
- let totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
- let trustMessage;
- if (rci.isTrusted) {
- trustMessage = (
- <i18n.Translate wrap="p">
- The exchange is trusted by the wallet.
- </i18n.Translate>
- );
- } else if (rci.isAudited) {
- trustMessage = (
- <i18n.Translate wrap="p">
- The exchange is audited by a trusted auditor.
- </i18n.Translate>
- );
- } else {
- trustMessage = (
- <i18n.Translate wrap="p">
- Warning: The exchange is neither directly trusted nor audited by a trusted auditor.
- If you withdraw from this exchange, it will be trusted in the future.
- </i18n.Translate>
- );
- }
- return (
- <div>
- <i18n.Translate wrap="p">
- Using exchange provider <strong>{this.url()}</strong>.
- The exchange provider will charge
- {" "}
- <span>{amountToPretty(totalCost)}</span>
- {" "}
- in fees.
- </i18n.Translate>
- {trustMessage}
- </div>
- );
- }
- if (this.url() && !this.statusString()) {
- let shortName = new URI(this.url()!).host();
- return (
- <i18n.Translate wrap="p">
- Waiting for a response from
- {" "}
- <em>{shortName}</em>
- </i18n.Translate>
- );
- }
- if (this.statusString()) {
- return (
- <p>
- <strong style={{color: "red"}}>{i18n.str`A problem occured, see below. ${this.statusString()}`}</strong>
- </p>
- );
- }
- return (
- <p>
- {i18n.str`Information about fees will be available when an exchange provider is selected.`}
- </p>
- );
- }
-
- renderConfirm() {
- return (
- <div>
- {this.renderFeeStatus()}
- <button className="pure-button button-success"
- disabled={this.reserveCreationInfo() == null}
- onClick={() => this.confirmReserve()}>
- {i18n.str`Accept fees and withdraw`}
- </button>
- { " " }
- <button className="pure-button button-secondary"
- onClick={() => this.selectingExchange(true)}>
- {i18n.str`Change Exchange Provider`}
- </button>
- <br/>
- <Collapsible initiallyCollapsed={true} title="Fee and Spending Details">
- {renderReserveCreationDetails(this.reserveCreationInfo())}
- </Collapsible>
- <Collapsible initiallyCollapsed={true} title="Auditor Details">
- {renderAuditorDetails(this.reserveCreationInfo())}
- </Collapsible>
- </div>
- );
- }
-
- select(url: string) {
- this.reserveCreationInfo(null);
- this.url(url);
- this.selectingExchange(false);
- this.forceReserveUpdate();
- }
-
- renderSelect() {
- let exchanges = (this.props.currencyRecord && this.props.currencyRecord.exchanges) || [];
- console.log(exchanges);
- return (
- <div>
- Please select an exchange. You can review the details before after your selection.
-
- {this.props.suggestedExchangeUrl && (
- <div>
- <h2>Bank Suggestion</h2>
- <button className="pure-button button-success" onClick={() => this.select(this.props.suggestedExchangeUrl)}>
- Select <strong>{this.props.suggestedExchangeUrl}</strong>
- </button>
- </div>
- )}
-
- {exchanges.length > 0 && (
- <div>
- <h2>Known Exchanges</h2>
- {exchanges.map(e => (
- <button className="pure-button button-success" onClick={() => this.select(e.baseUrl)}>
- Select <strong>{e.baseUrl}</strong>
- </button>
- ))}
- </div>
- )}
-
- <h2>Manual Selection</h2>
- <ManualSelection initialUrl={this.url() || ""} onSelect={(url: string) => this.select(url)} />
- </div>
- );
- }
-
- render(): JSX.Element {
- return (
- <div>
- <i18n.Translate wrap="p">
- {"You are about to withdraw "}
- <strong>{amountToPretty(this.props.amount)}</strong>
- {" from your bank account into your wallet."}
- </i18n.Translate>
- {this.selectingExchange() ? this.renderSelect() : this.renderConfirm()}
- </div>
- );
- }
-
-
- confirmReserve() {
- this.confirmReserveImpl(this.reserveCreationInfo()!,
- this.url()!,
- this.props.amount,
- this.props.callback_url);
- }
-
- /**
- * Do an update of the reserve creation info, without any debouncing.
- */
- async forceReserveUpdate() {
- this.reserveCreationInfo(null);
- try {
- let url = canonicalizeBaseUrl(this.url()!);
- let r = await getReserveCreationInfo(url,
- this.props.amount);
- console.log("get exchange info resolved");
- this.reserveCreationInfo(r);
- console.dir(r);
- } catch (e) {
- console.log("get exchange info rejected", e);
- if (e.hasOwnProperty("httpStatus")) {
- this.statusString(`Error: request failed with status ${e.httpStatus}`);
- } else if (e.hasOwnProperty("errorResponse")) {
- let resp = e.errorResponse;
- this.statusString(`Error: ${resp.error} (${resp.hint})`);
- }
- }
- }
-
- confirmReserveImpl(rci: ReserveCreationInfo,
- exchange: string,
- amount: AmountJson,
- callback_url: string) {
- const d = {exchange: canonicalizeBaseUrl(exchange), amount};
- const cb = (rawResp: any) => {
- if (!rawResp) {
- throw Error("empty response");
- }
- // FIXME: filter out types that bank/exchange don't have in common
- let wireDetails = rci.wireInfo;
- let filteredWireDetails: any = {};
- for (let wireType in wireDetails) {
- if (this.props.wt_types.findIndex((x) => x.toLowerCase() == wireType.toLowerCase()) < 0) {
- continue;
- }
- let obj = Object.assign({}, wireDetails[wireType]);
- // The bank doesn't need to know about fees
- delete obj.fees;
- // Consequently the bank can't verify signatures anyway, so
- // we delete this extra data, to make the request URL shorter.
- delete obj.salt;
- delete obj.sig;
- filteredWireDetails[wireType] = obj;
- }
- if (!rawResp.error) {
- const resp = CreateReserveResponse.checked(rawResp);
- let q: {[name: string]: string|number} = {
- wire_details: JSON.stringify(filteredWireDetails),
- exchange: resp.exchange,
- reserve_pub: resp.reservePub,
- amount_value: amount.value,
- amount_fraction: amount.fraction,
- amount_currency: amount.currency,
- };
- let url = new URI(callback_url).addQuery(q);
- if (!url.is("absolute")) {
- throw Error("callback url is not absolute");
- }
- console.log("going to", url.href());
- document.location.href = url.href();
- } else {
- this.statusString(
- i18n.str`Oops, something went wrong. The wallet responded with error status (${rawResp.error}).`);
- }
- };
- chrome.runtime.sendMessage({type: 'create-reserve', detail: d}, cb);
- }
-
- renderStatus(): any {
- if (this.statusString()) {
- return <p><strong style={{color: "red"}}>{this.statusString()}</strong></p>;
- } else if (!this.reserveCreationInfo()) {
- return <p>{i18n.str`Checking URL, please wait ...`}</p>;
- }
- return "";
- }
-}
-
-export async function main() {
- try {
- const url = new URI(document.location.href);
- const query: any = URI.parseQuery(url.query());
- let amount;
- try {
- amount = AmountJson.checked(JSON.parse(query.amount));
- } catch (e) {
- throw Error(i18n.str`Can't parse amount: ${e.message}`);
- }
- const callback_url = query.callback_url;
- const bank_url = query.bank_url;
- let wt_types;
- try {
- wt_types = JSON.parse(query.wt_types);
- } catch (e) {
- throw Error(i18n.str`Can't parse wire_types: ${e.message}`);
- }
-
- let suggestedExchangeUrl = query.suggested_exchange_url;
- let currencyRecord = await getCurrency(amount.currency);
-
- let args = {
- wt_types,
- suggestedExchangeUrl,
- callback_url,
- amount,
- currencyRecord,
- };
-
- ReactDOM.render(<ExchangeSelection {...args} />, document.getElementById(
- "exchange-selection")!);
-
- } catch (e) {
- // TODO: provide more context information, maybe factor it out into a
- // TODO:generic error reporting function or component.
- document.body.innerText = i18n.str`Fatal error: "${e.message}".`;
- console.error(`got error "${e.message}"`, e);
- }
-}
-
-document.addEventListener("DOMContentLoaded", () => {
- main();
-});
diff --git a/src/pages/error.html b/src/pages/error.html
deleted file mode 100644
index 51a8fd73a..000000000
--- a/src/pages/error.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Error Occured</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/error-bundle.js"></script>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/error.tsx b/src/pages/error.tsx
deleted file mode 100644
index f278bd224..000000000
--- a/src/pages/error.tsx
+++ /dev/null
@@ -1,63 +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 {ImplicitStateComponent, StateHolder} from "../components";
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-
-"use strict";
-
-interface ErrorProps {
- message: string;
-}
-
-class ErrorView extends React.Component<ErrorProps, void> {
- render(): JSX.Element {
- return (
- <div>
- An error occurred: {this.props.message}
- </div>
- );
- }
-}
-
-export async function main() {
- try {
- const url = new URI(document.location.href);
- const query: any = URI.parseQuery(url.query());
-
- const message: string = query.message || "unknown error";
-
- ReactDOM.render(<ErrorView message={message} />, document.getElementById(
- "container")!);
-
- } catch (e) {
- // TODO: provide more context information, maybe factor it out into a
- // TODO:generic error reporting function or component.
- document.body.innerText = `Fatal error: "${e.message}".`;
- console.error(`got error "${e.message}"`, e);
- }
-}
diff --git a/src/pages/help/empty-wallet.html b/src/pages/help/empty-wallet.html
deleted file mode 100644
index dd29d9689..000000000
--- a/src/pages/help/empty-wallet.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8">
- <title>GNU Taler Help - Empty Wallet</title>
- <link rel="icon" href="/img/icon.png">
- <meta name="description" content="">
- <link rel="stylesheet" type="text/css" href="/src/style/wallet.css">
- </head>
- <body>
- <div class="container" id="main">
- <div class="row">
- <div class="col-lg-12">
- <h2 lang="en">Your wallet is empty!</h2>
- <p lang="en">You have succeeded with installing the Taler wallet. However, before
- you can buy articles using the Taler wallet, you must withdraw electronic coins.
- This is typically done by visiting your bank's online banking Web site. There,
- you instruct your bank to transfer the funds to a Taler exchange operator. In
- return, your wallet will be allowed to withdraw electronic coins.</p>
- <p lang="en">At this stage, we are not aware of any regular exchange operators issuing
- coins in well-known currencies. However, to see how Taler would work, you
- can visit our "fake" bank at
- <a href="https://bank.demo.taler.net/">bank.demo.taler.net</a> to
- withdraw coins in the "KUDOS" currency that we created just for
- demonstrating the system.</p>
- </div>
- </div>
- </div>
- </body>
-</html>
diff --git a/src/pages/logs.html b/src/pages/logs.html
deleted file mode 100644
index 9545269e3..000000000
--- a/src/pages/logs.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Logs</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/logs-bundle.js"></script>
-
- <style>
- .tree-item {
- margin: 2em;
- border-radius: 5px;
- border: 1px solid gray;
- padding: 1em;
- }
- </style>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/logs.tsx b/src/pages/logs.tsx
deleted file mode 100644
index a1e5161ec..000000000
--- a/src/pages/logs.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 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/>
- */
-
-/**
- * Show wallet logs.
- *
- * @author Florian Dold
- */
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import {LogEntry, getLogs} from "../logging";
-
-interface LogViewProps {
- log: LogEntry;
-}
-
-class LogView extends React.Component<LogViewProps, void> {
- render(): JSX.Element {
- let e = this.props.log;
- return (
- <div className="tree-item">
- <ul>
- <li>level: {e.level}</li>
- <li>msg: {e.msg}</li>
- <li>id: {e.id || "unknown"}</li>
- <li>file: {e.source || "(unknown)"}</li>
- <li>line: {e.line || "(unknown)"}</li>
- <li>col: {e.col || "(unknown)"}</li>
- {(e.detail ? <li> detail: <pre>{e.detail}</pre></li> : [])}
- </ul>
- </div>
- );
- }
-}
-
-interface LogsState {
- logs: LogEntry[]|undefined;
-}
-
-class Logs extends React.Component<any, LogsState> {
- constructor() {
- super();
- this.update();
- this.state = {} as any;
- }
-
- async update() {
- let logs = await getLogs();
- this.setState({logs});
- }
-
- render(): JSX.Element {
- let logs = this.state.logs;
- if (!logs) {
- return <span>...</span>;
- }
- return (
- <div className="tree-item">
- Logs:
- {logs.map(e => <LogView log={e} />)}
- </div>
- );
- }
-}
-
-document.addEventListener("DOMContentLoaded", () => {
- ReactDOM.render(<Logs />, document.getElementById("container")!);
-});
diff --git a/src/pages/payback.html b/src/pages/payback.html
deleted file mode 100644
index d6fe334c8..000000000
--- a/src/pages/payback.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Payback</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/payback-bundle.js"></script>
-
- <style>
- body {
- font-size: 100%;
- }
- .tree-item {
- margin: 2em;
- border-radius: 5px;
- border: 1px solid gray;
- padding: 1em;
- }
- .button-linky {
- background: none;
- color: black;
- text-decoration: underline;
- border: none;
- }
- </style>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/payback.tsx b/src/pages/payback.tsx
deleted file mode 100644
index 01f5a64e4..000000000
--- a/src/pages/payback.tsx
+++ /dev/null
@@ -1,98 +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 {
- ExchangeRecord,
- ExchangeForCurrencyRecord,
- DenominationRecord,
- AuditorRecord,
- CurrencyRecord,
- ReserveRecord,
- CoinRecord,
- PreCoinRecord,
- Denomination,
- WalletBalance,
-} from "../types";
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
- getCurrencies,
- updateCurrency,
- getPaybackReserves,
- withdrawPaybackReserve,
-} from "../wxApi";
-import { amountToPretty, getTalerStampDate } from "../helpers";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-
-class Payback extends ImplicitStateComponent<any> {
- reserves: StateHolder<ReserveRecord[]|null> = this.makeState(null);
- constructor() {
- super();
- let port = chrome.runtime.connect();
- port.onMessage.addListener((msg: any) => {
- if (msg.notify) {
- console.log("got notified");
- this.update();
- }
- });
- this.update();
- }
-
- async update() {
- let reserves = await getPaybackReserves();
- this.reserves(reserves);
- }
-
- withdrawPayback(pub: string) {
- withdrawPaybackReserve(pub);
- }
-
- render(): JSX.Element {
- let reserves = this.reserves();
- if (!reserves) {
- return <span>loading ...</span>;
- }
- if (reserves.length == 0) {
- return <span>No reserves with payback available.</span>;
- }
- return (
- <div>
- {reserves.map(r => (
- <div>
- <h2>Reserve for ${amountToPretty(r.current_amount!)}</h2>
- <ul>
- <li>Exchange: ${r.exchange_base_url}</li>
- </ul>
- <button onClick={() => this.withdrawPayback(r.reserve_pub)}>Withdraw again</button>
- </div>
- ))}
- </div>
- );
- }
-}
-
-export function main() {
- ReactDOM.render(<Payback />, document.getElementById("container")!);
-}
-
-document.addEventListener("DOMContentLoaded", main);
diff --git a/src/pages/popup.css b/src/pages/popup.css
deleted file mode 100644
index 675412c11..000000000
--- a/src/pages/popup.css
+++ /dev/null
@@ -1,84 +0,0 @@
-
-/**
- * @author Gabor X. Toth
- * @author Marcello Stanisci
- * @author Florian Dold
- */
-
-body {
- min-height: 20em;
- width: 30em;
- margin: 0;
- padding: 0;
- max-height: 800px;
- overflow: hidden;
-}
-
-.nav {
- background-color: #ddd;
- padding: 0.5em 0;
-}
-
-.nav a {
- color: black;
- padding: 0.5em;
- text-decoration: none;
-}
-
-.nav a.active {
- background-color: white;
- font-weight: bold;
-}
-
-
-.container {
- overflow-y: scroll;
- max-height: 400px;
-}
-
-.abbrev {
- text-decoration-style: dotted;
-}
-
-#content {
- padding: 1em;
-}
-
-
-#wallet-table .amount {
- text-align: right;
-}
-
-.hidden {
- display: none;
-}
-
-#transactions-table th,
-#transactions-table td {
- padding: 0.2em 0.5em;
-}
-
-#reserve-create table {
- width: 100%;
-}
-
-#reserve-create table td.label {
- width: 5em;
-}
-
-#reserve-create table .input input[type="text"] {
- width: 100%;
-}
-
-.historyItem {
- border: 1px solid black;
- border-radius: 10px;
- padding-left: 0.5em;
- margin: 0.5em;
-}
-
-.historyDate {
- font-size: 90%;
- margin: 0.3em;
- color: slategray;
-}
diff --git a/src/pages/popup.html b/src/pages/popup.html
deleted file mode 100644
index 98f24bccc..000000000
--- a/src/pages/popup.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="utf-8">
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
- <link rel="stylesheet" type="text/css" href="popup.css">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/popup-bundle.js"></script>
-</head>
-
-<body>
- <div id="content" style="margin:0;padding:0"></div>
-</body>
-
-</html>
diff --git a/src/pages/popup.tsx b/src/pages/popup.tsx
deleted file mode 100644
index aef5a3df8..000000000
--- a/src/pages/popup.tsx
+++ /dev/null
@@ -1,545 +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
- */
-
-
-"use strict";
-
-import BrowserClickedEvent = chrome.browserAction.BrowserClickedEvent;
-import { HistoryRecord, HistoryLevel } from "../wallet";
-import {
- AmountJson, WalletBalance, Amounts,
- WalletBalanceEntry
-} from "../types";
-import { amountToPretty } from "../helpers";
-import { abbrev } from "../renderHtml";
-import * as i18n from "../i18n";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-import URI = require("urijs");
-
-function onUpdateNotification(f: () => void): () => void {
- let port = chrome.runtime.connect({name: "notifications"});
- let listener = (msg: any, port: any) => {
- f();
- };
- port.onMessage.addListener(listener);
- return () => {
- port.onMessage.removeListener(listener);
- }
-}
-
-
-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 () => {
- let i = Router.routeHandlers.indexOf(f);
- this.routeHandlers = this.routeHandlers.splice(i, 1);
- }
- }
-
- static routeHandlers: any[] = [];
-
- componentWillMount() {
- console.log("router mounted");
- window.onhashchange = () => {
- this.setState({});
- for (let f of Router.routeHandlers) {
- f();
- }
- }
- }
-
- componentWillUnmount() {
- console.log("router unmounted");
- }
-
-
- render(): JSX.Element {
- let 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) => {
- let childProps: any = (child as any).props;
- if (!childProps) {
- return;
- }
- if (childProps["default"]) {
- defaultChild = child;
- }
- if (childProps["route"] == route) {
- foundChild = child;
- }
- })
- let child: React.ReactChild | null = foundChild || defaultChild;
- if (!child) {
- throw Error("unknown route");
- }
- Router.setRoute((child as any).props["route"]);
- return <div>{child}</div>;
- }
-}
-
-
-interface TabProps {
- target: string;
- children?: React.ReactNode;
-}
-
-function Tab(props: TabProps) {
- let cssClass = "";
- if (props.target == Router.getRoute()) {
- cssClass = "active";
- }
- let onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
- 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> {
- cancelSubscription: any;
-
- componentWillMount() {
- this.cancelSubscription = Router.onRoute(() => {
- this.setState({});
- });
- }
-
- componentWillUnmount() {
- if (this.cancelSubscription) {
- this.cancelSubscription();
- }
- }
-
- render() {
- 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="/debug">
- {i18n.str`Debug`}
- </Tab>
- </div>);
- }
-}
-
-
-function ExtensionLink(props: any) {
- let onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
- chrome.tabs.create({
- "url": chrome.extension.getURL(props.target)
- });
- e.preventDefault();
- };
- return (
- <a onClick={onClick} href={props.target}>
- {props.children}
- </a>)
-}
-
-
-export function bigAmount(amount: AmountJson): JSX.Element {
- let v = amount.value + amount.fraction / Amounts.fractionalBase;
- return (
- <span>
- <span style={{fontSize: "300%"}}>{v}</span>
- {" "}
- <span>{amount.currency}</span>
- </span>
- );
-}
-
-class WalletBalanceView extends React.Component<any, any> {
- balance: WalletBalance;
- gotError = false;
- canceler: (() => void) | undefined = undefined;
- unmount = false;
-
- componentWillMount() {
- this.canceler = onUpdateNotification(() => this.updateBalance());
- this.updateBalance();
- }
-
- componentWillUnmount() {
- console.log("component WalletBalanceView will unmount");
- if (this.canceler) {
- this.canceler();
- }
- this.unmount = true;
- }
-
- updateBalance() {
- chrome.runtime.sendMessage({type: "balances"}, (resp) => {
- if (this.unmount) {
- return;
- }
- if (resp.error) {
- this.gotError = true;
- console.error("could not retrieve balances", resp);
- this.setState({});
- return;
- }
- this.gotError = false;
- console.log("got wallet", resp);
- this.balance = resp;
- this.setState({});
- });
- }
-
- renderEmpty(): JSX.Element {
- let helpLink = (
- <ExtensionLink target="/src/pages/help/empty-wallet.html">
- {i18n.str`help`}
- </ExtensionLink>
- );
- return (
- <div>
- <i18n.Translate wrap="p">
- You have no balance to show. Need some
- {" "}<span>{helpLink}</span>{" "}
- getting started?
- </i18n.Translate>
- </div>
- );
- }
-
- formatPending(entry: WalletBalanceEntry): JSX.Element {
- let incoming: JSX.Element | undefined;
- let payment: JSX.Element | undefined;
-
- console.log("available: ", entry.pendingIncoming ? amountToPretty(entry.available) : null);
- console.log("incoming: ", entry.pendingIncoming ? amountToPretty(entry.pendingIncoming) : null);
-
- if (Amounts.isNonZero(entry.pendingIncoming)) {
- incoming = (
- <i18n.Translate wrap="span">
- <span style={{color: "darkgreen"}}>
- {"+"}
- {amountToPretty(entry.pendingIncoming)}
- </span>
- {" "}
- incoming
- </i18n.Translate>
- );
- }
-
- if (Amounts.isNonZero(entry.pendingPayment)) {
- payment = (
- <i18n.Translate wrap="span">
- <span style={{color: "darkblue"}}>
- {amountToPretty(entry.pendingPayment)}
- </span>
- {" "}
- being spent
- </i18n.Translate>
- );
- }
-
- let 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 {
- let wallet = this.balance;
- if (this.gotError) {
- return i18n.str`Error: could not retrieve balance information.`;
- }
- if (!wallet) {
- return <span></span>;
- }
- console.log(wallet);
- let paybackAvailable = false;
- let listing = Object.keys(wallet).map((key) => {
- let entry: WalletBalanceEntry = wallet[key];
- if (entry.paybackAmount.value != 0 || entry.paybackAmount.fraction != 0) {
- paybackAvailable = true;
- }
- return (
- <p>
- {bigAmount(entry.available)}
- {" "}
- {this.formatPending(entry)}
- </p>
- );
- });
- let link = chrome.extension.getURL("/src/pages/auditors.html");
- let linkElem = <a className="actionLink" href={link} target="_blank">Trusted Auditors and Exchanges</a>;
- let paybackLink = chrome.extension.getURL("/src/pages/payback.html");
- let paybackLinkElem = <a className="actionLink" href={link} target="_blank">Trusted Auditors and Exchanges</a>;
- return (
- <div>
- {listing.length > 0 ? listing : this.renderEmpty()}
- {paybackAvailable && paybackLinkElem}
- {linkElem}
- </div>
- );
- }
-}
-
-
-function formatHistoryItem(historyItem: HistoryRecord) {
- const d = historyItem.detail;
- const t = historyItem.timestamp;
- console.log("hist item", historyItem);
- switch (historyItem.type) {
- case "create-reserve":
- return (
- <i18n.Translate wrap="p">
- Bank requested reserve (<span>{abbrev(d.reservePub)}</span>) for <span>{amountToPretty(d.requestedAmount)}</span>.
- </i18n.Translate>
- );
- case "confirm-reserve": {
- // FIXME: eventually remove compat fix
- let exchange = d.exchangeBaseUrl ? (new URI(d.exchangeBaseUrl)).host() : "??";
- let pub = abbrev(d.reservePub);
- return (
- <i18n.Translate wrap="p">
- Started to withdraw
- {" "}{amountToPretty(d.requestedAmount)}{" "}
- from <span>{exchange}</span> (<span>{pub}</span>).
- </i18n.Translate>
- );
- }
- case "offer-contract": {
- let link = chrome.extension.getURL("view-contract.html");
- let linkElem = <a href={link}>{abbrev(d.contractHash)}</a>;
- let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
- return (
- <i18n.Translate wrap="p">
- Merchant <em>{abbrev(d.merchantName, 15)}</em> offered contract <a href={link}>{abbrev(d.contractHash)}</a>;
- </i18n.Translate>
- );
- }
- case "depleted-reserve": {
- let exchange = d.exchangeBaseUrl ? (new URI(d.exchangeBaseUrl)).host() : "??";
- let amount = amountToPretty(d.requestedAmount);
- let pub = abbrev(d.reservePub);
- return (
- <i18n.Translate wrap="p">
- Withdrew <span>{amount}</span> from <span>{exchange}</span> (<span>{pub}</span>).
- </i18n.Translate>
- );
- }
- case "pay": {
- let url = d.fulfillmentUrl;
- let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
- let fulfillmentLinkElem = <a href={url} onClick={openTab(url)}>view product</a>;
- return (
- <i18n.Translate wrap="p">
- Paid <span>{amountToPretty(d.amount)}</span> to merchant <span>{merchantElem}</span>. (<span>{fulfillmentLinkElem}</span>)
- </i18n.Translate>
- );
- }
- default:
- return (<p>{i18n.str`Unknown event (${historyItem.type})`}</p>);
- }
-}
-
-
-class WalletHistory extends React.Component<any, any> {
- myHistory: any[];
- gotError = false;
- unmounted = false;
-
- componentWillMount() {
- this.update();
- onUpdateNotification(() => this.update());
- }
-
- componentWillUnmount() {
- console.log("history component unmounted");
- this.unmounted = true;
- }
-
- update() {
- chrome.runtime.sendMessage({type: "get-history"}, (resp) => {
- if (this.unmounted) {
- return;
- }
- console.log("got history response");
- if (resp.error) {
- this.gotError = true;
- console.error("could not retrieve history", resp);
- this.setState({});
- return;
- }
- this.gotError = false;
- console.log("got history", resp.history);
- this.myHistory = resp.history;
- this.setState({});
- });
- }
-
- render(): JSX.Element {
- console.log("rendering history");
- let history: HistoryRecord[] = this.myHistory;
- if (this.gotError) {
- return i18n.str`Error: could not retrieve event history`;
- }
-
- if (!history) {
- // We're not ready yet
- return <span />;
- }
-
- let subjectMemo: {[s: string]: boolean} = {};
- let listing: any[] = [];
- for (let record of history.reverse()) {
- if (record.subjectId && subjectMemo[record.subjectId]) {
- continue;
- }
- if (record.level != undefined && record.level < HistoryLevel.User) {
- continue;
- }
- subjectMemo[record.subjectId as string] = true;
-
- let item = (
- <div className="historyItem">
- <div className="historyDate">
- {(new Date(record.timestamp)).toString()}
- </div>
- {formatHistoryItem(record)}
- </div>
- );
-
- listing.push(item);
- }
-
- if (listing.length > 0) {
- return <div className="container">{listing}</div>;
- }
- return <p>{i18n.str`Your wallet has no events recorded.`}</p>
- }
-
-}
-
-
-function reload() {
- try {
- chrome.runtime.reload();
- window.close();
- } catch (e) {
- // Functionality missing in firefox, ignore!
- }
-}
-
-function confirmReset() {
- if (confirm("Do you want to IRREVOCABLY DESTROY everything inside your" +
- " wallet and LOSE ALL YOUR COINS?")) {
- chrome.runtime.sendMessage({type: "reset"});
- window.close();
- }
-}
-
-
-function WalletDebug(props: any) {
- return (<div>
- <p>Debug tools:</p>
- <button onClick={openExtensionPage("/src/pages/popup.html")}>
- wallet tab
- </button>
- <button onClick={openExtensionPage("/src/pages/show-db.html")}>
- show db
- </button>
- <button onClick={openExtensionPage("/src/pages/tree.html")}>
- show tree
- </button>
- <button onClick={openExtensionPage("/src/pages/logs.html")}>
- show logs
- </button>
- <br />
- <button onClick={confirmReset}>
- reset
- </button>
- <button onClick={reload}>
- reload chrome extension
- </button>
- </div>);
-}
-
-
-function openExtensionPage(page: string) {
- return function() {
- chrome.tabs.create({
- "url": chrome.extension.getURL(page)
- });
- }
-}
-
-
-function openTab(page: string) {
- return function() {
- chrome.tabs.create({
- "url": page
- });
- }
-}
-
-
-let el = (
- <div>
- <WalletNavBar />
- <div style={{margin: "1em"}}>
- <Router>
- <WalletBalanceView route="/balance" default/>
- <WalletHistory route="/history"/>
- <WalletDebug route="/debug"/>
- </Router>
- </div>
- </div>
-);
-
-document.addEventListener("DOMContentLoaded", () => {
- ReactDOM.render(el, document.getElementById("content")!);
-})
diff --git a/src/pages/show-db.html b/src/pages/show-db.html
deleted file mode 100644
index 215c726d9..000000000
--- a/src/pages/show-db.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!doctype html>
-<html>
- <head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Reserve Created</title>
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
- <link rel="icon" href="/img/icon.png">
- <script src="/dist/page-common.js"></script>
- <script src="/dist/show-db-bundle.js"></script>
- </head>
- <body>
- <h1>DB Dump</h1>
- <input type="file" id="fileInput" style="display:none">
- <button id="import">Import Dump</button>
- <button id="download">Download Dump</button>
- <pre id="dump"></pre>
- </body>
-</html>
diff --git a/src/pages/show-db.ts b/src/pages/show-db.ts
deleted file mode 100644
index d95951385..000000000
--- a/src/pages/show-db.ts
+++ /dev/null
@@ -1,94 +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/>
- */
-
-
-/**
- * Wallet database dump for debugging.
- *
- * @author Florian Dold
- */
-
-function replacer(match: string, pIndent: string, pKey: string, pVal: string,
- pEnd: string) {
- const key = "<span class=json-key>";
- const val = "<span class=json-value>";
- const str = "<span class=json-string>";
- let r = pIndent || "";
- if (pKey) {
- r = r + key + '"' + pKey.replace(/[": ]/g, "") + '":</span> ';
- }
- if (pVal) {
- r = r + (pVal[0] === '"' ? str : val) + pVal + "</span>";
- }
- return r + (pEnd || "");
-}
-
-
-function prettyPrint(obj: any) {
- const jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
- return JSON.stringify(obj, null as any, 3)
- .replace(/&/g, "&amp;").replace(/\\"/g, "&quot;")
- .replace(/</g, "&lt;").replace(/>/g, "&gt;")
- .replace(jsonLine, replacer);
-}
-
-
-document.addEventListener("DOMContentLoaded", () => {
- chrome.runtime.sendMessage({type: "dump-db"}, (resp) => {
- const el = document.getElementById("dump");
- if (!el) {
- throw Error();
- }
- el.innerHTML = prettyPrint(resp);
-
- document.getElementById("download")!.addEventListener("click", (evt) => {
- console.log("creating download");
- const element = document.createElement("a");
- element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(JSON.stringify(resp)));
- element.setAttribute("download", "wallet-dump.txt");
- element.style.display = "none";
- document.body.appendChild(element);
- element.click();
- });
-
- });
-
-
- const fileInput = document.getElementById("fileInput")! as HTMLInputElement;
- fileInput.onchange = (evt) => {
- if (!fileInput.files || fileInput.files.length !== 1) {
- alert("please select exactly one file to import");
- return;
- }
- const file = fileInput.files[0];
- const fr = new FileReader();
- fr.onload = (e: any) => {
- console.log("got file");
- const dump = JSON.parse(e.target.result);
- console.log("parsed contents", dump);
- chrome.runtime.sendMessage({ type: "import-db", detail: { dump } }, (resp) => {
- alert("loaded");
- });
- };
- console.log("reading file", file);
- fr.readAsText(file);
- };
-
- document.getElementById("import")!.addEventListener("click", (evt) => {
- fileInput.click();
- evt.preventDefault();
- });
-});
diff --git a/src/pages/tree.html b/src/pages/tree.html
deleted file mode 100644
index 0c0a368b3..000000000
--- a/src/pages/tree.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
- <meta charset="UTF-8">
- <title>Taler Wallet: Tree View</title>
-
- <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
- <link rel="icon" href="/img/icon.png">
-
- <script src="/dist/page-common-bundle.js"></script>
- <script src="/dist/tree-bundle.js"></script>
-
- <style>
- .tree-item {
- margin: 2em;
- border-radius: 5px;
- border: 1px solid gray;
- padding: 1em;
- }
- </style>
-
- <body>
- <div id="container"></div>
- </body>
-</html>
diff --git a/src/pages/tree.tsx b/src/pages/tree.tsx
deleted file mode 100644
index 8d1258c51..000000000
--- a/src/pages/tree.tsx
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 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/>
- */
-
-/**
- * Show contents of the wallet as a tree.
- *
- * @author Florian Dold
- */
-
-
-import {
- ExchangeRecord,
- DenominationRecord,
- CoinStatus,
- ReserveRecord,
- CoinRecord,
- PreCoinRecord,
- Denomination,
-} from "../types";
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
- getReserves, getExchanges, getCoins, getPreCoins,
- refresh, getDenoms, payback,
-} from "../wxApi";
-import { amountToPretty, getTalerStampDate } from "../helpers";
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-
-interface ReserveViewProps {
- reserve: ReserveRecord;
-}
-
-class ReserveView extends React.Component<ReserveViewProps, void> {
- render(): JSX.Element {
- let r: ReserveRecord = this.props.reserve;
- return (
- <div className="tree-item">
- <ul>
- <li>Key: {r.reserve_pub}</li>
- <li>Created: {(new Date(r.created * 1000).toString())}</li>
- <li>Current: {r.current_amount ? amountToPretty(r.current_amount!) : "null"}</li>
- <li>Requested: {amountToPretty(r.requested_amount)}</li>
- <li>Confirmed: {r.confirmed}</li>
- </ul>
- </div>
- );
- }
-}
-
-interface ReserveListProps {
- exchangeBaseUrl: string;
-}
-
-interface ToggleProps {
- expanded: StateHolder<boolean>;
-}
-
-class Toggle extends ImplicitStateComponent<ToggleProps> {
- renderButton() {
- let show = () => {
- this.props.expanded(true);
- this.setState({});
- };
- let hide = () => {
- this.props.expanded(false);
- this.setState({});
- };
- if (this.props.expanded()) {
- return <button onClick={hide}>hide</button>;
- }
- return <button onClick={show}>show</button>;
-
- }
- render() {
- return (
- <div style={{display: "inline"}}>
- {this.renderButton()}
- {this.props.expanded() ? this.props.children : []}
- </div>);
- }
-}
-
-
-interface CoinViewProps {
- coin: CoinRecord;
-}
-
-interface RefreshDialogProps {
- coin: CoinRecord;
-}
-
-class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> {
- refreshRequested = this.makeState<boolean>(false);
- render(): JSX.Element {
- if (!this.refreshRequested()) {
- return (
- <div style={{display: "inline"}}>
- <button onClick={() => this.refreshRequested(true)}>refresh</button>
- </div>
- );
- }
- return (
- <div>
- Refresh amount: <input type="text" size={10} />
- <button onClick={() => refresh(this.props.coin.coinPub)}>ok</button>
- <button onClick={() => this.refreshRequested(false)}>cancel</button>
- </div>
- );
- }
-}
-
-class CoinView extends React.Component<CoinViewProps, void> {
- render() {
- let c = this.props.coin;
- return (
- <div className="tree-item">
- <ul>
- <li>Key: {c.coinPub}</li>
- <li>Current amount: {amountToPretty(c.currentAmount)}</li>
- <li>Denomination: <ExpanderText text={c.denomPub} /></li>
- <li>Suspended: {(c.suspended || false).toString()}</li>
- <li>Status: {CoinStatus[c.status]}</li>
- <li><RefreshDialog coin={c} /></li>
- <li><button onClick={() => payback(c.coinPub)}>Payback</button></li>
- </ul>
- </div>
- );
- }
-}
-
-
-
-interface PreCoinViewProps {
- precoin: PreCoinRecord;
-}
-
-class PreCoinView extends React.Component<PreCoinViewProps, void> {
- render() {
- let c = this.props.precoin;
- return (
- <div className="tree-item">
- <ul>
- <li>Key: {c.coinPub}</li>
- </ul>
- </div>
- );
- }
-}
-
-interface CoinListProps {
- exchangeBaseUrl: string;
-}
-
-class CoinList extends ImplicitStateComponent<CoinListProps> {
- coins = this.makeState<CoinRecord[] | null>(null);
- expanded = this.makeState<boolean>(false);
-
- constructor(props: CoinListProps) {
- super(props);
- this.update(props);
- }
-
- async update(props: CoinListProps) {
- let coins = await getCoins(props.exchangeBaseUrl);
- this.coins(coins);
- }
-
- componentWillReceiveProps(newProps: CoinListProps) {
- this.update(newProps);
- }
-
- render(): JSX.Element {
- if (!this.coins()) {
- return <div>...</div>;
- }
- return (
- <div className="tree-item">
- Coins ({this.coins() !.length.toString()})
- {" "}
- <Toggle expanded={this.expanded}>
- {this.coins() !.map((c) => <CoinView coin={c} />)}
- </Toggle>
- </div>
- );
- }
-}
-
-
-interface PreCoinListProps {
- exchangeBaseUrl: string;
-}
-
-class PreCoinList extends ImplicitStateComponent<PreCoinListProps> {
- precoins = this.makeState<PreCoinRecord[] | null>(null);
- expanded = this.makeState<boolean>(false);
-
- constructor(props: PreCoinListProps) {
- super(props);
- this.update();
- }
-
- async update() {
- let precoins = await getPreCoins(this.props.exchangeBaseUrl);
- this.precoins(precoins);
- }
-
- render(): JSX.Element {
- if (!this.precoins()) {
- return <div>...</div>;
- }
- return (
- <div className="tree-item">
- Planchets ({this.precoins() !.length.toString()})
- {" "}
- <Toggle expanded={this.expanded}>
- {this.precoins() !.map((c) => <PreCoinView precoin={c} />)}
- </Toggle>
- </div>
- );
- }
-}
-
-interface DenominationListProps {
- exchange: ExchangeRecord;
-}
-
-interface ExpanderTextProps {
- text: string;
-}
-
-class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
- expanded = this.makeState<boolean>(false);
- textArea: any = undefined;
-
- componentDidUpdate() {
- if (this.expanded() && this.textArea) {
- this.textArea.focus();
- this.textArea.scrollTop = 0;
- }
- }
-
- render(): JSX.Element {
- if (!this.expanded()) {
- return (
- <span onClick={() => { this.expanded(true); }}>
- {(this.props.text.length <= 10)
- ? this.props.text
- : (
- <span>
- {this.props.text.substring(0,10)}
- <span style={{textDecoration: "underline"}}>...</span>
- </span>
- )
- }
- </span>
- );
- }
- return (
- <textarea
- readOnly
- style={{display: "block"}}
- onBlur={() => this.expanded(false)}
- ref={(e) => this.textArea = e}>
- {this.props.text}
- </textarea>
- );
- }
-}
-
-class DenominationList extends ImplicitStateComponent<DenominationListProps> {
- expanded = this.makeState<boolean>(false);
- denoms = this.makeState<undefined|DenominationRecord[]>(undefined);
-
- constructor(props: DenominationListProps) {
- super(props);
- this.update();
- }
-
- async update() {
- let d = await getDenoms(this.props.exchange.baseUrl);
- this.denoms(d);
- }
-
- renderDenom(d: DenominationRecord) {
- return (
- <div className="tree-item">
- <ul>
- <li>Offered: {d.isOffered ? "yes" : "no"}</li>
- <li>Value: {amountToPretty(d.value)}</li>
- <li>Withdraw fee: {amountToPretty(d.feeWithdraw)}</li>
- <li>Refresh fee: {amountToPretty(d.feeRefresh)}</li>
- <li>Deposit fee: {amountToPretty(d.feeDeposit)}</li>
- <li>Refund fee: {amountToPretty(d.feeRefund)}</li>
- <li>Start: {getTalerStampDate(d.stampStart)!.toString()}</li>
- <li>Withdraw expiration: {getTalerStampDate(d.stampExpireWithdraw)!.toString()}</li>
- <li>Legal expiration: {getTalerStampDate(d.stampExpireLegal)!.toString()}</li>
- <li>Deposit expiration: {getTalerStampDate(d.stampExpireDeposit)!.toString()}</li>
- <li>Denom pub: <ExpanderText text={d.denomPub} /></li>
- </ul>
- </div>
- );
- }
-
- render(): JSX.Element {
- let denoms = this.denoms()
- if (!denoms) {
- return (
- <div className="tree-item">
- Denominations (...)
- {" "}
- <Toggle expanded={this.expanded}>
- ...
- </Toggle>
- </div>
- );
- }
- return (
- <div className="tree-item">
- Denominations ({denoms.length.toString()})
- {" "}
- <Toggle expanded={this.expanded}>
- {denoms.map((d) => this.renderDenom(d))}
- </Toggle>
- </div>
- );
- }
-}
-
-class ReserveList extends ImplicitStateComponent<ReserveListProps> {
- reserves = this.makeState<ReserveRecord[] | null>(null);
- expanded = this.makeState<boolean>(false);
-
- constructor(props: ReserveListProps) {
- super(props);
- this.update();
- }
-
- async update() {
- let reserves = await getReserves(this.props.exchangeBaseUrl);
- this.reserves(reserves);
- }
-
- render(): JSX.Element {
- if (!this.reserves()) {
- return <div>...</div>;
- }
- return (
- <div className="tree-item">
- Reserves ({this.reserves() !.length.toString()})
- {" "}
- <Toggle expanded={this.expanded}>
- {this.reserves() !.map((r) => <ReserveView reserve={r} />)}
- </Toggle>
- </div>
- );
- }
-}
-
-interface ExchangeProps {
- exchange: ExchangeRecord;
-}
-
-class ExchangeView extends React.Component<ExchangeProps, void> {
- render(): JSX.Element {
- let e = this.props.exchange;
- return (
- <div className="tree-item">
- <ul>
- <li>Exchange Base Url: {this.props.exchange.baseUrl}</li>
- <li>Master public key: <ExpanderText text={this.props.exchange.masterPublicKey} /></li>
- </ul>
- <DenominationList exchange={e} />
- <ReserveList exchangeBaseUrl={this.props.exchange.baseUrl} />
- <CoinList exchangeBaseUrl={this.props.exchange.baseUrl} />
- <PreCoinList exchangeBaseUrl={this.props.exchange.baseUrl} />
- </div>
- );
- }
-}
-
-interface ExchangesListState {
- exchanges?: ExchangeRecord[];
-}
-
-class ExchangesList extends React.Component<any, ExchangesListState> {
- constructor() {
- super();
- let 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() {
- let exchanges = await getExchanges();
- console.log("exchanges: ", exchanges);
- this.setState({ exchanges });
- }
-
- render(): JSX.Element {
- let exchanges = this.state.exchanges;
- if (!exchanges) {
- return <span>...</span>;
- }
- return (
- <div className="tree-item">
- Exchanges ({exchanges.length.toString()}):
- {exchanges.map(e => <ExchangeView exchange={e} />)}
- </div>
- );
- }
-}
-
-export function main() {
- ReactDOM.render(<ExchangesList />, document.getElementById("container")!);
-}
-
-document.addEventListener("DOMContentLoaded", main);