gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-core] branch master updated: welcome page wit


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-core] branch master updated: welcome page with error diagnostics / react refactoring
Date: Thu, 05 Sep 2019 16:10:55 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 8144b0f5 welcome page with error diagnostics / react refactoring
8144b0f5 is described below

commit 8144b0f5535c3d00c1e508cddce3cd88a153a581
Author: Florian Dold <address@hidden>
AuthorDate: Thu Sep 5 16:10:53 2019 +0200

    welcome page with error diagnostics / react refactoring
---
 Makefile                                        |   4 +
 src/db.ts                                       |   2 +-
 src/dbTypes.ts                                  |   2 +-
 src/i18n/de.po                                  |  16 +-
 src/i18n/en-US.po                               |  16 +-
 src/i18n/fr.po                                  |  16 +-
 src/i18n/it.po                                  |  16 +-
 src/i18n/sv.po                                  |  16 +-
 src/i18n/taler-wallet-webex.pot                 |  16 +-
 src/walletTypes.ts                              |  18 +-
 src/webex/components.ts                         |  63 ----
 src/webex/messages.ts                           |   5 +
 src/webex/pages/add-auditor.tsx                 | 119 +++----
 src/webex/pages/help/empty-wallet.html          |  30 --
 src/webex/pages/payback.tsx                     |  87 ++---
 src/webex/pages/popup.tsx                       | 255 +++++++--------
 src/webex/pages/tree.html                       |  27 --
 src/webex/pages/tree.tsx                        | 402 ------------------------
 src/webex/pages/{withdraw.html => welcome.html} |   8 +-
 src/webex/pages/welcome.tsx                     | 113 +++++++
 src/webex/pages/withdraw.html                   |   2 +-
 src/webex/pages/withdraw.tsx                    |  10 +-
 src/webex/renderHtml.tsx                        |  82 +++--
 src/webex/wxApi.ts                              |   8 +
 src/webex/wxBackend.ts                          |  88 ++++--
 tsconfig.json                                   |   3 +-
 webpack.config.js                               |   2 +-
 27 files changed, 515 insertions(+), 911 deletions(-)

diff --git a/Makefile b/Makefile
index f29f8108..b33ac71b 100644
--- a/Makefile
+++ b/Makefile
@@ -86,3 +86,7 @@ install: tsc
        npm install -g --prefix $(prefix) .     
 endif
 
+.PHONY: watch
+watch: tsconfig.json
+
+       ./node_modules/.bin/webpack --watch
diff --git a/src/db.ts b/src/db.ts
index eaac22eb..00eac432 100644
--- a/src/db.ts
+++ b/src/db.ts
@@ -17,7 +17,7 @@ export function openTalerDb(
     const req = idbFactory.open(DB_NAME, WALLET_DB_VERSION);
     req.onerror = e => {
       console.log("taler database error", e);
-      reject(e);
+      reject(new Error("database error"));
     };
     req.onsuccess = e => {
       req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
diff --git a/src/dbTypes.ts b/src/dbTypes.ts
index 0ca2b626..ef79ae19 100644
--- a/src/dbTypes.ts
+++ b/src/dbTypes.ts
@@ -43,7 +43,7 @@ import { Index, Store } from "./query";
  * In the future we might consider adding migration functions for
  * each version increment.
  */
-export const WALLET_DB_VERSION = 26;
+export const WALLET_DB_VERSION = 27;
 
 /**
  * A reserve record as stored in the wallet's database.
diff --git a/src/i18n/de.po b/src/i18n/de.po
index 6a27556c..9d068790 100644
--- a/src/i18n/de.po
+++ b/src/i18n/de.po
@@ -250,42 +250,42 @@ msgstr ""
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, fuzzy, c-format
 msgid "Withdrawal fees:"
 msgstr "Abheben bei"
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, fuzzy, c-format
 msgid "Withdraw Fee"
 msgstr "Abheben bei %1$s"
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
diff --git a/src/i18n/en-US.po b/src/i18n/en-US.po
index 558ec717..0f73aa14 100644
--- a/src/i18n/en-US.po
+++ b/src/i18n/en-US.po
@@ -241,42 +241,42 @@ msgstr ""
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
diff --git a/src/i18n/fr.po b/src/i18n/fr.po
index ba7f9ecf..ee6b66eb 100644
--- a/src/i18n/fr.po
+++ b/src/i18n/fr.po
@@ -241,42 +241,42 @@ msgstr ""
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
diff --git a/src/i18n/it.po b/src/i18n/it.po
index ba7f9ecf..ee6b66eb 100644
--- a/src/i18n/it.po
+++ b/src/i18n/it.po
@@ -241,42 +241,42 @@ msgstr ""
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
diff --git a/src/i18n/sv.po b/src/i18n/sv.po
index 23e1a364..97e510f4 100644
--- a/src/i18n/sv.po
+++ b/src/i18n/sv.po
@@ -245,42 +245,42 @@ msgstr "Acceptera avgifter och utbetala"
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, c-format
 msgid "Withdrawal fees:"
 msgstr "Utbetalnings avgifter:"
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr "# Mynt"
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr "Värde"
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, c-format
 msgid "Withdraw Fee"
 msgstr "Utbetalnings avgift"
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr "Återhämtnings avgift"
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr "Depostitions avgift"
diff --git a/src/i18n/taler-wallet-webex.pot b/src/i18n/taler-wallet-webex.pot
index ba7f9ecf..ee6b66eb 100644
--- a/src/i18n/taler-wallet-webex.pot
+++ b/src/i18n/taler-wallet-webex.pot
@@ -241,42 +241,42 @@ msgstr ""
 msgid "Cancel withdraw operation"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:225
+#: src/webex/renderHtml.tsx:226
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:226
+#: src/webex/renderHtml.tsx:227
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:227
+#: src/webex/renderHtml.tsx:228
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:233
+#: src/webex/renderHtml.tsx:234
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:234
+#: src/webex/renderHtml.tsx:235
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:235
+#: src/webex/renderHtml.tsx:236
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:236
+#: src/webex/renderHtml.tsx:237
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:237
+#: src/webex/renderHtml.tsx:238
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index 0d18e4a9..47360c66 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -507,8 +507,16 @@ export interface AcceptWithdrawalResponse {
  * Details about a purchase, including refund status.
  */
 export interface PurchaseDetails {
-  contractTerms: ContractTerms,
-  hasRefund: boolean,
-  totalRefundAmount: AmountJson,
-  totalRefundAndRefreshFees: AmountJson,
-}
\ No newline at end of file
+  contractTerms: ContractTerms;
+  hasRefund: boolean;
+  totalRefundAmount: AmountJson;
+  totalRefundAndRefreshFees: AmountJson;
+}
+
+export interface WalletDiagnostics {
+  walletManifestVersion: string;
+  walletManifestDisplayVersion: string;
+  errors: string[];
+  firefoxIdbProblem: boolean;
+  dbOutdated: boolean;
+}
diff --git a/src/webex/components.ts b/src/webex/components.ts
deleted file mode 100644
index 1f5d1873..00000000
--- a/src/webex/components.ts
+++ /dev/null
@@ -1,63 +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/>
- */
-
-
-/**
- * General helper React components.
- */
-
-
-/**
- * Imports.
- */
-import * as React from "react";
-
-/**
- * Wrapper around state that will cause updates to the
- * containing component.
- */
-export interface StateHolder<T> {
-  (): T;
-  (newState: T): void;
-}
-
-/**
- * Component that doesn't hold its state in one object,
- * but has multiple state holders.
- */
-export abstract class ImplicitStateComponent<PropType> extends 
React.Component<PropType, any> {
-  private _implicit = {needsUpdate: false, didMount: false};
-  componentDidMount() {
-    this._implicit.didMount = true;
-    if (this._implicit.needsUpdate) {
-      this.setState({} as any);
-    }
-  }
-  makeState<StateType>(initial: StateType): StateHolder<StateType> {
-    let state: StateType = initial;
-    return (s?: StateType): StateType => {
-      if (s !== undefined) {
-        state = s;
-        if (this._implicit.didMount) {
-          this.setState({} as any);
-        } else {
-          this._implicit.needsUpdate = true;
-        }
-      }
-      return state;
-    };
-  }
-}
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 7e99cfc7..27d85a1f 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -205,6 +205,11 @@ export interface MessageMap {
     request: { talerPayUri: string };
     response: walletTypes.PreparePayResult;
   };
+
+  "get-diagnostics": {
+    request: { };
+    response: walletTypes.WalletDiagnostics;
+  };
 }
 
 
diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx
index 1ab6fdf9..7e3e0632 100644
--- a/src/webex/pages/add-auditor.tsx
+++ b/src/webex/pages/add-auditor.tsx
@@ -20,20 +20,11 @@
  * @author Florian Dold
  */
 
-
-import {
-  CurrencyRecord,
-} from "../../dbTypes";
-
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
-  getCurrencies,
-  updateCurrency,
-} from "../wxApi";
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
+import { CurrencyRecord } from "../../dbTypes";
+import { getCurrencies, updateCurrency } from "../wxApi";
+import React, { useState } from "react";
 import URI = require("urijs");
+import { registerMountPage } from "../renderHtml";
 
 interface ConfirmAuditorProps {
   url: string;
@@ -42,36 +33,39 @@ interface ConfirmAuditorProps {
   expirationStamp: number;
 }
 
-class ConfirmAuditor extends ImplicitStateComponent<ConfirmAuditorProps> {
-  private addDone: StateHolder<boolean> = this.makeState(false);
-  constructor(props: ConfirmAuditorProps) {
-    super(props);
-  }
+function ConfirmAuditor(props: ConfirmAuditorProps) {
+  const [addDone, setAddDone] = useState(false);
+
 
-  async add() {
+  const add = async() => {
     const currencies = await getCurrencies();
-    let currency: CurrencyRecord|undefined;
+    let currency: CurrencyRecord | undefined;
 
     for (const c of currencies) {
-      if (c.name === this.props.currency) {
+      if (c.name === props.currency) {
         currency = c;
       }
     }
 
     if (!currency) {
-      currency = { name: this.props.currency, auditors: [], fractionalDigits: 
2, exchanges: [] };
+      currency = {
+        name: props.currency,
+        auditors: [],
+        fractionalDigits: 2,
+        exchanges: [],
+      };
     }
 
     const newAuditor = {
-      auditorPub: this.props.auditorPub,
-      baseUrl: this.props.url,
-      expirationStamp: this.props.expirationStamp,
+      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 === this.props.url) {
+      if (a.baseUrl === props.url) {
         auditorFound = true;
         // Update auditor if already found by URL.
         currency.auditors[idx] = newAuditor;
@@ -84,47 +78,54 @@ class ConfirmAuditor extends 
ImplicitStateComponent<ConfirmAuditorProps> {
 
     await updateCurrency(currency);
 
-    this.addDone(true);
+    setAddDone(true);
   }
 
-  back() {
+  const 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/webex/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>
-    );
-  }
+  };
+
+  return (
+    <div id="main">
+      <p>
+        Do you want to let <strong>{props.auditorPub}</strong> audit the
+        currency "{props.currency}"?
+      </p>
+      {addDone ? (
+        <div>
+          Auditor was added! You can also{" "}
+          <a href={chrome.extension.getURL("/src/webex/pages/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>
+  );
 }
 
-function main() {
+
+registerMountPage(() => {
   const walletPageUrl = new URI(document.location.href);
-  const query: any = JSON.parse((URI.parseQuery(walletPageUrl.query()) as 
any).req);
+  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);
+  return <ConfirmAuditor {...args}/>;
+});
diff --git a/src/webex/pages/help/empty-wallet.html 
b/src/webex/pages/help/empty-wallet.html
deleted file mode 100644
index dd29d968..00000000
--- a/src/webex/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/webex/pages/payback.tsx b/src/webex/pages/payback.tsx
index f69a3349..934c28c0 100644
--- a/src/webex/pages/payback.tsx
+++ b/src/webex/pages/payback.tsx
@@ -20,73 +20,54 @@
  * @author Florian Dold
  */
 
-
 /**
  * Imports.
  */
-import {
-  ReserveRecord,
-} from "../../dbTypes";
+import { ReserveRecord } from "../../dbTypes";
+import { renderAmount, registerMountPage } from "../renderHtml";
+import { getPaybackReserves, withdrawPaybackReserve } from "../wxApi";
+import * as React from "react";
+import { useState } from "react";
 
-import { ImplicitStateComponent, StateHolder } from "../components";
-import { renderAmount } from "../renderHtml";
-import {
-  getPaybackReserves,
-  withdrawPaybackReserve,
-} from "../wxApi";
+function Payback() {
+  const [reserves, setReserves] = useState<ReserveRecord[] | null>(null);
 
-import * as React from "react";
-import * as ReactDOM from "react-dom";
+  useState(() => {
+    const update = async () => {
+      const r = await getPaybackReserves();
+      setReserves(r);
+    };
 
-class Payback extends ImplicitStateComponent<{}> {
-  private reserves: StateHolder<ReserveRecord[]|null> = this.makeState(null);
-  constructor(props: {}) {
-    super(props);
     const port = chrome.runtime.connect();
     port.onMessage.addListener((msg: any) => {
       if (msg.notify) {
         console.log("got notified");
-        this.update();
+        update();
       }
     });
-    this.update();
-  }
+  });
 
-  async update() {
-    const reserves = await getPaybackReserves();
-    this.reserves(reserves);
+  if (!reserves) {
+    return <span>loading ...</span>;
   }
-
-  withdrawPayback(pub: string) {
-    withdrawPaybackReserve(pub);
+  if (reserves.length === 0) {
+    return <span>No reserves with payback available.</span>;
   }
-
-  render(): JSX.Element {
-    const 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 ${renderAmount(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>
-    );
-  }
-}
-
-function main() {
-  ReactDOM.render(<Payback />, document.getElementById("container")!);
+  return (
+    <div>
+      {reserves.map(r => (
+        <div>
+          <h2>Reserve for ${renderAmount(r.current_amount!)}</h2>
+          <ul>
+            <li>Exchange: ${r.exchange_base_url}</li>
+          </ul>
+          <button onClick={() => withdrawPaybackReserve(r.reserve_pub)}>
+            Withdraw again
+          </button>
+        </div>
+      ))}
+    </div>
+  );
 }
 
-document.addEventListener("DOMContentLoaded", main);
+registerMountPage(() => <Payback />);
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index 2cdfd823..91ab515e 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -14,7 +14,6 @@
  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.
@@ -38,7 +37,7 @@ import {
   WalletBalanceEntry,
 } from "../../walletTypes";
 
-import { abbrev, renderAmount } from "../renderHtml";
+import { abbrev, renderAmount, PageLink } from "../renderHtml";
 import * as wxApi from "../wxApi";
 
 import * as React from "react";
@@ -47,7 +46,7 @@ import * as ReactDOM from "react-dom";
 import URI = require("urijs");
 
 function onUpdateNotification(f: () => void): () => void {
-  const port = chrome.runtime.connect({name: "notifications"});
+  const port = chrome.runtime.connect({ name: "notifications" });
   const listener = () => {
     f();
   };
@@ -57,7 +56,6 @@ function onUpdateNotification(f: () => void): () => void {
   };
 }
 
-
 class Router extends React.Component<any, any> {
   static setRoute(s: string): void {
     window.location.hash = s;
@@ -92,13 +90,12 @@ class Router extends React.Component<any, any> {
     console.log("router unmounted");
   }
 
-
   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) => {
+    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;
@@ -119,7 +116,6 @@ class Router extends React.Component<any, any> {
   }
 }
 
-
 interface TabProps {
   target: string;
   children?: React.ReactNode;
@@ -141,7 +137,6 @@ function Tab(props: TabProps) {
   );
 }
 
-
 class WalletNavBar extends React.Component<any, any> {
   private cancelSubscription: any;
 
@@ -161,20 +156,14 @@ class WalletNavBar extends React.Component<any, any> {
     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>);
+        <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) {
   const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
     chrome.tabs.create({
@@ -189,7 +178,6 @@ function ExtensionLink(props: any) {
   );
 }
 
-
 /**
  * Render an amount as a large number with a small currency symbol.
  */
@@ -197,10 +185,21 @@ function bigAmount(amount: AmountJson): JSX.Element {
   const v = amount.value + amount.fraction / Amounts.fractionalBase;
   return (
     <span>
-      <span style={{fontSize: "300%"}}>{v}</span>
-      {" "}
+      <span style={{ fontSize: "300%" }}>{v}</span>{" "}
       <span>{amount.currency}</span>
-      </span>
+    </span>
+  );
+}
+
+function EmptyBalanceView() {
+  return (
+    <div>
+      <i18n.Translate wrap="p">
+        You have no balance to show. Need some{" "}
+        <PageLink pageName="welcome.html">help</PageLink> getting
+        started?
+      </i18n.Translate>
+    </div>
   );
 }
 
@@ -245,57 +244,44 @@ class WalletBalanceView extends React.Component<any, any> 
{
     this.setState({});
   }
 
-  renderEmpty(): JSX.Element {
-    const helpLink = (
-      <ExtensionLink target="/src/webex/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 ? 
renderAmount(entry.available) : null);
-    console.log("incoming: ", entry.pendingIncoming ? 
renderAmount(entry.pendingIncoming) : null);
+    console.log(
+      "available: ",
+      entry.pendingIncoming ? renderAmount(entry.available) : null,
+    );
+    console.log(
+      "incoming: ",
+      entry.pendingIncoming ? renderAmount(entry.pendingIncoming) : null,
+    );
 
     if (Amounts.isNonZero(entry.pendingIncoming)) {
       incoming = (
         <i18n.Translate wrap="span">
-          <span style={{color: "darkgreen"}}>
+          <span style={{ color: "darkgreen" }}>
             {"+"}
             {renderAmount(entry.pendingIncoming)}
-          </span>
-          {" "}
+          </span>{" "}
           incoming
-      </i18n.Translate>
+        </i18n.Translate>
       );
     }
 
     if (Amounts.isNonZero(entry.pendingPayment)) {
       payment = (
         <i18n.Translate wrap="span">
-          <span style={{color: "red"}}>
+          <span style={{ color: "red" }}>
             {"-"}
             {renderAmount(entry.pendingPayment)}
-          </span>
-          {" "}
+          </span>{" "}
           being spent
         </i18n.Translate>
       );
     }
 
-    const l = [incoming, payment].filter((x) => x !== undefined);
+    const l = [incoming, payment].filter(x => x !== undefined);
     if (l.length === 0) {
       return <span />;
     }
@@ -303,49 +289,41 @@ class WalletBalanceView extends React.Component<any, any> 
{
     if (l.length === 1) {
       return <span>({l})</span>;
     }
-    return <span>({l[0]}, {l[1]})</span>;
-
+    return (
+      <span>
+        ({l[0]}, {l[1]})
+      </span>
+    );
   }
 
   render(): JSX.Element {
     const wallet = this.balance;
     if (this.gotError) {
-      return i18n.str`Error: could not retrieve balance information.`;
+      return (
+        <div>
+          <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);
-    let paybackAvailable = false;
-    const listing = Object.keys(wallet.byCurrency).map((key) => {
+    const listing = Object.keys(wallet.byCurrency).map(key => {
       const entry: WalletBalanceEntry = wallet.byCurrency[key];
-      if (entry.paybackAmount.value !== 0 || entry.paybackAmount.fraction !== 
0) {
-        paybackAvailable = true;
-      }
       return (
         <p>
-          {bigAmount(entry.available)}
-          {" "}
-          {this.formatPending(entry)}
+          {bigAmount(entry.available)} {this.formatPending(entry)}
         </p>
       );
     });
-    const makeLink = (page: string, name: string) => {
-      const url = chrome.extension.getURL(`/src/webex/pages/${page}`);
-      return <div><a className="actionLink" href={url} 
target="_blank">{name}</a></div>;
-    };
-    return (
-      <div>
-        {listing.length > 0 ? listing : this.renderEmpty()}
-        {paybackAvailable && makeLink("payback", i18n.str`Payback`)}
-        {makeLink("return-coins.html#dissolve", i18n.str`Return Electronic 
Cash to Bank Account`)}
-        {makeLink("auditors.html", i18n.str`Manage Trusted Auditors and 
Exchanges`)}
-      </div>
-    );
+    return <div>{listing.length > 0 ? listing : <EmptyBalanceView />}</div>;
   }
 }
 
-
 function formatHistoryItem(historyItem: HistoryRecord) {
   const d = historyItem.detail;
   console.log("hist item", historyItem);
@@ -353,13 +331,12 @@ function formatHistoryItem(historyItem: HistoryRecord) {
     case "create-reserve":
       return (
         <i18n.Translate wrap="p">
-          Bank requested reserve (<span>{abbrev(d.reservePub)}</span>) for
-          {" "}
+          Bank requested reserve (<span>{abbrev(d.reservePub)}</span>) for{" "}
           <span>{renderAmount(d.requestedAmount)}</span>.
         </i18n.Translate>
       );
     case "confirm-reserve": {
-      const exchange = (new URI(d.exchangeBaseUrl)).host();
+      const exchange = new URI(d.exchangeBaseUrl).host();
       const pub = abbrev(d.reservePub);
       return (
         <i18n.Translate wrap="p">
@@ -372,30 +349,37 @@ function formatHistoryItem(historyItem: HistoryRecord) {
     case "offer-contract": {
       return (
         <i18n.Translate wrap="p">
-          Merchant <em>{abbrev(d.merchantName, 15)}</em> offered
-          contract <span>{abbrev(d.contractTermsHash)}</span>.
+          Merchant <em>{abbrev(d.merchantName, 15)}</em> offered contract{" "}
+          <span>{abbrev(d.contractTermsHash)}</span>.
         </i18n.Translate>
       );
     }
     case "depleted-reserve": {
-      const exchange = d.exchangeBaseUrl ? (new URI(d.exchangeBaseUrl)).host() 
: "??";
+      const exchange = d.exchangeBaseUrl
+        ? new URI(d.exchangeBaseUrl).host()
+        : "??";
       const amount = renderAmount(d.requestedAmount);
       const pub = abbrev(d.reservePub);
       return (
         <i18n.Translate wrap="p">
-          Withdrew <span>{amount}</span> from <span>{exchange}</span> 
(<span>{pub}</span>).
+          Withdrew <span>{amount}</span> from <span>{exchange}</span> (
+          <span>{pub}</span>).
         </i18n.Translate>
       );
     }
     case "pay": {
       const url = d.fulfillmentUrl;
       const merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
-      const fulfillmentLinkElem = <a href={url} onClick={openTab(url)}>view 
product</a>;
+      const fulfillmentLinkElem = (
+        <a href={url} onClick={openTab(url)}>
+          view product
+        </a>
+      );
       return (
         <i18n.Translate wrap="p">
-          Paid <span>{renderAmount(d.amount)}</span> to merchant 
<span>{merchantElem}</span>.
-          <span> </span>
-          (<span>{fulfillmentLinkElem}</span>)
+          Paid <span>{renderAmount(d.amount)}</span> to merchant{" "}
+          <span>{merchantElem}</span>.<span> </span>(
+          <span>{fulfillmentLinkElem}</span>)
         </i18n.Translate>
       );
     }
@@ -403,12 +387,15 @@ function formatHistoryItem(historyItem: HistoryRecord) {
       const merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
       return (
         <i18n.Translate wrap="p">
-          Merchant <span>{merchantElem}</span> gave a refund over 
<span>{renderAmount(d.refundAmount)}</span>.
+          Merchant <span>{merchantElem}</span> gave a refund over{" "}
+          <span>{renderAmount(d.refundAmount)}</span>.
         </i18n.Translate>
       );
     }
     case "tip": {
-      const tipPageUrl = new 
URI(chrome.extension.getURL("/src/webex/pages/tip.html"));
+      const tipPageUrl = new URI(
+        chrome.extension.getURL("/src/webex/pages/tip.html"),
+      );
       const params = { tip_id: d.tipId, merchant_domain: d.merchantDomain };
       const url = tipPageUrl.query(params).href();
       const tipLink = <a href={url} onClick={openTab(url)}>{i18n.str`tip`}</a>;
@@ -416,19 +403,23 @@ function formatHistoryItem(historyItem: HistoryRecord) {
       return (
         <>
           <i18n.Translate wrap="p">
-            Merchant <span>{d.merchantDomain}</span> gave
-            a <span>{tipLink}</span> of <span>{renderAmount(d.amount)}</span>.
+            Merchant <span>{d.merchantDomain}</span> gave a{" "}
+            <span>{tipLink}</span> of <span>{renderAmount(d.amount)}</span>.
           </i18n.Translate>
-          <span> { d.accepted ? null : <i18n.Translate>You did not accept the 
tip yet.</i18n.Translate> }</span>
+          <span>
+            {" "}
+            {d.accepted ? null : (
+              <i18n.Translate>You did not accept the tip yet.</i18n.Translate>
+            )}
+          </span>
         </>
       );
     }
     default:
-      return (<p>{i18n.str`Unknown event (${historyItem.type})`}</p>);
+      return <p>{i18n.str`Unknown event (${historyItem.type})`}</p>;
   }
 }
 
-
 class WalletHistory extends React.Component<any, any> {
   private myHistory: any[];
   private gotError = false;
@@ -445,7 +436,7 @@ class WalletHistory extends React.Component<any, any> {
   }
 
   update() {
-    chrome.runtime.sendMessage({type: "get-history"}, (resp) => {
+    chrome.runtime.sendMessage({ type: "get-history" }, resp => {
       if (this.unmounted) {
         return;
       }
@@ -480,7 +471,7 @@ class WalletHistory extends React.Component<any, any> {
       const item = (
         <div className="historyItem">
           <div className="historyDate">
-            {(new Date(record.timestamp)).toString()}
+            {new Date(record.timestamp).toString()}
           </div>
           {formatHistoryItem(record)}
         </div>
@@ -494,10 +485,8 @@ class WalletHistory extends React.Component<any, any> {
     }
     return <p>{i18n.str`Your wallet has no events recorded.`}</p>;
   }
-
 }
 
-
 function reload() {
   try {
     chrome.runtime.reload();
@@ -508,43 +497,43 @@ function reload() {
 }
 
 function confirmReset() {
-  if (confirm("Do you want to IRREVOCABLY DESTROY everything inside your" +
-              " wallet and LOSE ALL YOUR COINS?")) {
+  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) {
-  return (<div>
-    <p>Debug tools:</p>
-    <button onClick={openExtensionPage("/src/webex/pages/popup.html")}>
-      wallet tab
-    </button>
-    <button onClick={openExtensionPage("/src/webex/pages/benchmark.html")}>
-      benchmark
-    </button>
-    <button onClick={openExtensionPage("/src/webex/pages/show-db.html")}>
-      show db
-    </button>
-    <button onClick={openExtensionPage("/src/webex/pages/tree.html")}>
-      show tree
-    </button>
-    <button onClick={openExtensionPage("/src/webex/pages/logs.html")}>
-      show logs
-    </button>
-    <br />
-    <button onClick={confirmReset}>
-      reset
-    </button>
-    <button onClick={reload}>
-      reload chrome extension
-    </button>
-  </div>);
+  return (
+    <div>
+      <p>Debug tools:</p>
+      <button onClick={openExtensionPage("/src/webex/pages/popup.html")}>
+        wallet tab
+      </button>
+      <button onClick={openExtensionPage("/src/webex/pages/benchmark.html")}>
+        benchmark
+      </button>
+      <button onClick={openExtensionPage("/src/webex/pages/show-db.html")}>
+        show db
+      </button>
+      <button onClick={openExtensionPage("/src/webex/pages/tree.html")}>
+        show tree
+      </button>
+      <button onClick={openExtensionPage("/src/webex/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 () => {
     chrome.tabs.create({
@@ -553,7 +542,6 @@ function openExtensionPage(page: string) {
   };
 }
 
-
 function openTab(page: string) {
   return (evt: React.SyntheticEvent<any>) => {
     evt.preventDefault();
@@ -563,15 +551,14 @@ function openTab(page: string) {
   };
 }
 
-
 const el = (
   <div>
     <WalletNavBar />
-    <div style={{margin: "1em"}}>
+    <div style={{ margin: "1em" }}>
       <Router>
-        <WalletBalanceView route="/balance" default/>
-        <WalletHistory route="/history"/>
-        <WalletDebug route="/debug"/>
+        <WalletBalanceView route="/balance" default />
+        <WalletHistory route="/history" />
+        <WalletDebug route="/debug" />
       </Router>
     </div>
   </div>
@@ -581,5 +568,5 @@ runOnceWhenReady(() => {
   ReactDOM.render(el, document.getElementById("content")!);
   // Will be used by the backend to detect when the popup gets closed,
   // so we can clear notifications
-  chrome.runtime.connect({name: "popup"});
+  chrome.runtime.connect({ name: "popup" });
 });
diff --git a/src/webex/pages/tree.html b/src/webex/pages/tree.html
deleted file mode 100644
index 0c0a368b..00000000
--- a/src/webex/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/webex/pages/tree.tsx b/src/webex/pages/tree.tsx
deleted file mode 100644
index 67e58a1d..00000000
--- a/src/webex/pages/tree.tsx
+++ /dev/null
@@ -1,402 +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 { getTalerStampDate } from "../../helpers";
-
-import {
-  CoinRecord,
-  CoinStatus,
-  DenominationRecord,
-  ExchangeRecord,
-  PreCoinRecord,
-  ReserveRecord,
-} from "../../dbTypes";
-
-import { ImplicitStateComponent, StateHolder } from "../components";
-import {
-  getCoins,
-  getDenoms,
-  getExchanges,
-  getPreCoins,
-  getReserves,
-  payback,
-  refresh,
-} from "../wxApi";
-
-import { ExpanderText, renderAmount } from "../renderHtml";
-
-import * as React from "react";
-import * as ReactDOM from "react-dom";
-
-interface ReserveViewProps {
-  reserve: ReserveRecord;
-}
-
-class ReserveView extends React.Component<ReserveViewProps, {}> {
-  render(): JSX.Element {
-    const 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 ? renderAmount(r.current_amount!) : 
"null"}</li>
-          <li>Requested: {renderAmount(r.requested_amount)}</li>
-          <li>Confirmed: {r.timestamp_confirmed}</li>
-        </ul>
-      </div>
-    );
-  }
-}
-
-interface ReserveListProps {
-  exchangeBaseUrl: string;
-}
-
-interface ToggleProps {
-  expanded: StateHolder<boolean>;
-}
-
-class Toggle extends ImplicitStateComponent<ToggleProps> {
-  renderButton() {
-    const show = () => {
-      this.props.expanded(true);
-      this.setState({});
-    };
-    const 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> {
-  private 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, {}> {
-  render() {
-    const c = this.props.coin;
-    return (
-      <div className="tree-item">
-        <ul>
-          <li>Key: {c.coinPub}</li>
-          <li>Current amount: {renderAmount(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, {}> {
-  render() {
-    const 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> {
-  private coins = this.makeState<CoinRecord[] | null>(null);
-  private expanded = this.makeState<boolean>(false);
-
-  constructor(props: CoinListProps) {
-    super(props);
-    this.update(props);
-  }
-
-  async update(props: CoinListProps) {
-    const 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> {
-  private precoins = this.makeState<PreCoinRecord[] | null>(null);
-  private expanded = this.makeState<boolean>(false);
-
-  constructor(props: PreCoinListProps) {
-    super(props);
-    this.update();
-  }
-
-  async update() {
-    const 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;
-}
-
-class DenominationList extends ImplicitStateComponent<DenominationListProps> {
-  private expanded = this.makeState<boolean>(false);
-  private denoms = this.makeState<undefined|DenominationRecord[]>(undefined);
-
-  constructor(props: DenominationListProps) {
-    super(props);
-    this.update();
-  }
-
-  async update() {
-    const 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: {renderAmount(d.value)}</li>
-          <li>Withdraw fee: {renderAmount(d.feeWithdraw)}</li>
-          <li>Refresh fee: {renderAmount(d.feeRefresh)}</li>
-          <li>Deposit fee: {renderAmount(d.feeDeposit)}</li>
-          <li>Refund fee: {renderAmount(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 {
-    const 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> {
-  private reserves = this.makeState<ReserveRecord[] | null>(null);
-  private expanded = this.makeState<boolean>(false);
-
-  constructor(props: ReserveListProps) {
-    super(props);
-    this.update();
-  }
-
-  async update() {
-    const 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, {}> {
-  render(): JSX.Element {
-    const 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<{}, ExchangesListState> {
-  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() {
-    const exchanges = await getExchanges();
-    console.log("exchanges: ", exchanges);
-    this.setState({ exchanges });
-  }
-
-  render(): JSX.Element {
-    const 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>
-    );
-  }
-}
-
-function main() {
-  ReactDOM.render(<ExchangesList />, document.getElementById("container")!);
-}
-
-document.addEventListener("DOMContentLoaded", main);
diff --git a/src/webex/pages/withdraw.html b/src/webex/pages/welcome.html
similarity index 65%
copy from src/webex/pages/withdraw.html
copy to src/webex/pages/welcome.html
index 8b1e59b1..9a96d04a 100644
--- a/src/webex/pages/withdraw.html
+++ b/src/webex/pages/welcome.html
@@ -3,21 +3,21 @@
 
 <head>
   <meta charset="UTF-8">
-  <title>Taler Wallet: Select Taler Provider</title>
+  <title>Taler Wallet: Withdraw</title>
 
   <link rel="icon" href="/img/icon.png">
   <link rel="stylesheet" type="text/css" href="../style/pure.css">
   <link rel="stylesheet" type="text/css" href="../style/wallet.css">
 
   <script src="/dist/page-common-bundle.js"></script>
-  <script src="/dist/withdraw-bundle.js"></script>
+  <script src="/dist/welcome-bundle.js"></script>
 
 </head>
 
 <body>
   <section id="main">
-    <h1>GNU Taler Wallet</h1>
-      <div class="fade" id="exchange-selection"></div>
+    <h1>GNU Taler Wallet Installed!</h1>
+    <div id="container">Loading...</div>
   </section>
 </body>
 
diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx
new file mode 100644
index 00000000..1026e6e6
--- /dev/null
+++ b/src/webex/pages/welcome.tsx
@@ -0,0 +1,113 @@
+/*
+ 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 { registerMountPage, PageLink } from "../renderHtml";
+import { WalletDiagnostics } from "../../walletTypes";
+
+function Diagnostics() {
+  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 () => {
+      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 <p>Running diagnostics ... everything looks fine.</p>;
+    } 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>{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>;
+}
+
+function Welcome() {
+  return (
+    <>
+      <p>Thank you for installing the wallet.</p>
+      <h2>First Steps</h2>
+      <p>
+        Check out <a href="https://demo.taler.net/";>demo.taler.net</a> for a
+        demo.
+      </p>
+      <h2>Troubleshooting</h2>
+      <Diagnostics />
+    </>
+  );
+}
+
+registerMountPage(() => <Welcome />);
diff --git a/src/webex/pages/withdraw.html b/src/webex/pages/withdraw.html
index 8b1e59b1..e5c52727 100644
--- a/src/webex/pages/withdraw.html
+++ b/src/webex/pages/withdraw.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="UTF-8">
-  <title>Taler Wallet: Select Taler Provider</title>
+  <title>Taler Wallet: Withdraw</title>
 
   <link rel="icon" href="/img/icon.png">
   <link rel="stylesheet" type="text/css" href="../style/pure.css">
diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx
index 66617373..39b27f2d 100644
--- a/src/webex/pages/withdraw.tsx
+++ b/src/webex/pages/withdraw.tsx
@@ -21,21 +21,13 @@
  * @author Florian Dold
  */
 
-import { canonicalizeBaseUrl } from "../../helpers";
-import * as i18n from "../../i18n";
 
-import { AmountJson } from "../../amounts";
-import * as Amounts from "../../amounts";
+import * as i18n from "../../i18n";
 
-import { CurrencyRecord } from "../../dbTypes";
 import {
-  CreateReserveResponse,
-  ReserveCreationInfo,
   WithdrawDetails,
 } from "../../walletTypes";
 
-import { ImplicitStateComponent, StateHolder } from "../components";
-
 import { WithdrawDetailView, renderAmount } from "../renderHtml";
 
 import React, { useState, useEffect } from "react";
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index 1c50aa1a..0f736d1b 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -26,22 +26,16 @@
  */
 import { AmountJson } from "../amounts";
 import * as Amounts from "../amounts";
-
 import {
   DenominationRecord,
 } from "../dbTypes";
 import {
   ReserveCreationInfo,
 } from "../walletTypes";
-
-
-import { ImplicitStateComponent } from "./components";
-
 import * as moment from "moment";
-
 import * as i18n from "../i18n";
-
-import * as React from "react";
+import React from "react";
+import ReactDOM from "react-dom";
 
 
 /**
@@ -274,49 +268,16 @@ interface ExpanderTextProps {
   text: string;
 }
 
+
 /**
  * Show a heading with a toggle to show/hide the expandable content.
  */
-export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
-  private expanded = this.makeState<boolean>(false);
-  private 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>
-    );
-  }
+export function ExpanderText({ text }: ExpanderTextProps) {
+  return <span>{text}</span>;
 }
 
 
+
 export interface LoadingButtonProps {
   loading: boolean;
 }
@@ -340,4 +301,35 @@ export function ProgressButton(
       {props.children}
     </button>
   );
+}
+
+export function registerMountPage(mainFn: () => React.ReactElement) {
+  async function main() {
+    try {
+    const mainElement = mainFn();
+    const container = document.getElementById("container");
+    if (!container) {
+      throw Error("container not found, can't mount page contents");
+    }
+      ReactDOM.render(
+        mainElement,
+        container,
+      );
+    } catch (e) {
+      document.body.innerText = `Fatal error: "${e.message}".  Please report 
this bug at https://bugs.gnunet.org/.`;
+      console.error("got error", e);
+    }
+  }
+
+  if (document.readyState === "loading") {
+    document.addEventListener("DOMContentLoaded", main);
+    return;
+  } else {
+    main();
+  }
+}
+
+export function PageLink(props: React.PropsWithChildren<{pageName: string}>) {
+  const url = chrome.extension.getURL(`/src/webex/pages/${props.pageName}`);
+  return <a className="actionLink" href={url} 
target="_blank">{props.children}</a>;
 }
\ No newline at end of file
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 7e4d17e3..61dc2ca6 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -42,6 +42,7 @@ import {
   TipStatus,
   WalletBalance,
   PurchaseDetails,
+  WalletDiagnostics,
 } from "../walletTypes";
 
 import {
@@ -396,3 +397,10 @@ export function preparePay(talerPayUri: string) {
 export function acceptWithdrawal(talerWithdrawUri: string, selectedExchange: 
string) {
   return callBackend("accept-withdrawal", { talerWithdrawUri, selectedExchange 
});
 }
+
+/**
+ * Get diagnostics information
+ */
+export function getDiagnostics(): Promise<WalletDiagnostics> {
+  return callBackend("get-diagnostics", {});
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index ea43f65c..0cfaf234 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -25,40 +25,34 @@
  */
 import { BrowserHttpLib } from "../http";
 import * as logging from "../logging";
-
 import { AmountJson } from "../amounts";
-
 import {
   ConfirmReserveRequest,
   CreateReserveRequest,
   Notifier,
   ReturnCoinsRequest,
+  WalletDiagnostics,
 } from "../walletTypes";
-
 import { Wallet } from "../wallet";
-
 import { isFirefox } from "./compat";
-
-import { PurchaseRecord, WALLET_DB_VERSION } from "../dbTypes";
-
+import { WALLET_DB_VERSION } from "../dbTypes";
 import { openTalerDb, exportDb, importDb, deleteDb } from "../db";
-
 import { ChromeBadge } from "./chromeBadge";
 import { MessageType } from "./messages";
 import * as wxApi from "./wxApi";
-
 import URI = require("urijs");
 import Port = chrome.runtime.Port;
 import MessageSender = chrome.runtime.MessageSender;
 import { BrowserCryptoWorkerFactory } from "../crypto/cryptoApi";
+import { OpenedPromise, openPromise } from "../promiseUtils";
 
 const NeedsWallet = Symbol("NeedsWallet");
 
-function handleMessage(
+async function handleMessage(
   sender: MessageSender,
   type: MessageType,
   detail: any,
-): any {
+): Promise<any> {
   function assertNotFound(t: never): never {
     console.error(`Request type ${t as string} unknown`);
     console.error(`Request detail was ${detail}`);
@@ -251,7 +245,7 @@ function handleMessage(
       const resp: wxApi.UpgradeResponse = {
         currentDbVersion: WALLET_DB_VERSION.toString(),
         dbResetRequired,
-        oldDbVersion: (oldDbVersion || "unknown").toString(),
+        oldDbVersion: (outdatedDbVersion || "unknown").toString(),
       };
       return resp;
     }
@@ -314,6 +308,39 @@ function handleMessage(
         detail.selectedExchange,
       );
     }
+    case "get-diagnostics": {
+      const manifestData = chrome.runtime.getManifest();
+      const errors: string[] = [];
+      let firefoxIdbProblem = false;
+      let dbOutdated = false;
+      try {
+        await walletInit.promise;
+      } catch (e) {
+        errors.push("Error during wallet initialization: " + e);
+        if (currentDatabase === undefined && outdatedDbVersion === undefined 
&& isFirefox()) {
+          firefoxIdbProblem = true;
+        }
+      }
+      if (!currentWallet) {
+        errors.push("Could not create wallet backend.");
+      }
+      if (!currentDatabase) {
+        errors.push("Could not open database");
+      }
+      if (outdatedDbVersion !== undefined) {
+        errors.push(`Outdated DB version: ${outdatedDbVersion}`);
+        dbOutdated = true;
+      }
+      const diagnostics: WalletDiagnostics = {
+        walletManifestDisplayVersion:
+          manifestData.version_name || "(undefined)",
+        walletManifestVersion: manifestData.version,
+        errors,
+        firefoxIdbProblem,
+        dbOutdated,
+      };
+      return diagnostics;
+    }
     case "prepare-pay":
       return needsWallet().preparePay(detail.talerPayUri);
     default:
@@ -351,7 +378,7 @@ async function dispatch(
         error: {
           message: e.message,
           stack,
-        }
+        },
       });
     } catch (e) {
       console.log(e);
@@ -441,26 +468,24 @@ function makeSyncWalletRedirect(
   return { redirectUrl: outerUrl.href() };
 }
 
-// Rate limit cache for executePayment operations, to break redirect loops
-let rateLimitCache: { [n: number]: number } = {};
-
-function clearRateLimitCache() {
-  rateLimitCache = {};
-}
-
 /**
  * Currently active wallet instance.  Might be unloaded and
  * re-instantiated when the database is reset.
  */
 let currentWallet: Wallet | undefined;
 
+let currentDatabase: IDBDatabase | undefined;
+
 /**
  * Last version if an outdated DB, if applicable.
  */
-let oldDbVersion: number | undefined;
+let outdatedDbVersion: number | undefined;
+
+let walletInit: OpenedPromise<void> = openPromise<void>();
 
 function handleUpgradeUnsupported(oldDbVersion: number, newDbVersion: number) {
   console.log("DB migration not supported");
+  outdatedDbVersion = oldDbVersion;
   chrome.tabs.create({
     url: chrome.extension.getURL("/src/webex/pages/reset-required.html"),
   });
@@ -473,20 +498,25 @@ async function reinitWallet() {
     currentWallet.stop();
     currentWallet = undefined;
   }
+  currentDatabase = undefined;
   setBadgeText({ text: "" });
   const badge = new ChromeBadge();
-  let db: IDBDatabase;
   try {
-    db = await openTalerDb(indexedDB, reinitWallet, handleUpgradeUnsupported);
+    currentDatabase = await openTalerDb(
+      indexedDB,
+      reinitWallet,
+      handleUpgradeUnsupported,
+    );
   } catch (e) {
     console.error("could not open database", e);
+    walletInit.reject(e);
     return;
   }
   const http = new BrowserHttpLib();
   const notifier = new ChromeNotifier();
   console.log("setting wallet");
   const wallet = new Wallet(
-    db,
+    currentDatabase,
     http,
     badge,
     notifier,
@@ -495,6 +525,7 @@ async function reinitWallet() {
   // Useful for debugging in the background page.
   (window as any).talerWallet = wallet;
   currentWallet = wallet;
+  walletInit.resolve();
 }
 
 /**
@@ -528,6 +559,13 @@ function injectScript(
  * Sets up all event handlers and other machinery.
  */
 export async function wxMain() {
+  chrome.runtime.onInstalled.addListener(details => {
+    if (details.reason === "install") {
+      const url = chrome.extension.getURL("/src/webex/pages/welcome.html");
+      chrome.tabs.create({ active: true, url: url });
+    }
+  });
+
   // Explicitly unload the extension page as soon as an update is available,
   // so the update gets installed as soon as possible.
   chrome.runtime.onUpdateAvailable.addListener(details => {
@@ -630,8 +668,6 @@ export async function wxMain() {
     tabTimers[tabId] = timers;
   });
 
-  chrome.extension.getBackgroundPage()!.setInterval(clearRateLimitCache, 5000);
-
   reinitWallet();
 
   // Handlers for messages coming directly from the content
diff --git a/tsconfig.json b/tsconfig.json
index 7aca0791..30791876 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -66,7 +66,6 @@
     "src/webex/background.ts",
     "src/webex/chromeBadge.ts",
     "src/webex/compat.ts",
-    "src/webex/components.ts",
     "src/webex/messages.ts",
     "src/webex/notify.ts",
     "src/webex/pages/add-auditor.tsx",
@@ -84,7 +83,7 @@
     "src/webex/pages/return-coins.tsx",
     "src/webex/pages/show-db.ts",
     "src/webex/pages/tip.tsx",
-    "src/webex/pages/tree.tsx",
+    "src/webex/pages/welcome.tsx",
     "src/webex/pages/withdraw.tsx",
     "src/webex/renderHtml.tsx",
     "src/webex/wxApi.ts",
diff --git a/webpack.config.js b/webpack.config.js
index df9ebbcf..09645588 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -79,6 +79,7 @@ module.exports = function (env) {
       "benchmark": "./src/webex/pages/benchmark.tsx",
       "pay": "./src/webex/pages/pay.tsx",
       "withdraw": "./src/webex/pages/withdraw.tsx",
+      "welcome": "./src/webex/pages/welcome.tsx",
       "error": "./src/webex/pages/error.tsx",
       "logs": "./src/webex/pages/logs.tsx",
       "payback": "./src/webex/pages/payback.tsx",
@@ -88,7 +89,6 @@ module.exports = function (env) {
       "refund": "./src/webex/pages/refund.tsx",
       "show-db": "./src/webex/pages/show-db.ts",
       "tip": "./src/webex/pages/tip.tsx",
-      "tree": "./src/webex/pages/tree.tsx",
     },
     name: "pages",
     optimization: {

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]