gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated: pretty reset pa


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: pretty reset page
Date: Mon, 05 Jun 2017 03:20:30 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 29045913 pretty reset page
29045913 is described below

commit 290459133226503bcbc16f7dc3cb04d7abdc6735
Author: Florian Dold <address@hidden>
AuthorDate: Mon Jun 5 03:20:28 2017 +0200

    pretty reset page
---
 src/webex/messages.ts               |   6 +-
 src/webex/pages/popup.tsx           |   3 +-
 src/webex/pages/reset-required.html |  30 ++++++
 src/webex/pages/reset-required.tsx  |  73 +++++++++++++
 src/webex/style/wallet.css          |   4 -
 src/webex/wxApi.ts                  |  35 ++++++
 src/webex/wxBackend.ts              | 208 +++++++++++++++++++++++-------------
 tsconfig.json                       |   1 +
 webpack.config.js                   |   1 +
 9 files changed, 280 insertions(+), 81 deletions(-)

diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 27ff9a5b..bf9ca00b 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -49,7 +49,7 @@ export interface MessageMap {
     request: { };
     response: void;
   };
-  "reset": {
+  "reset-db": {
     request: { };
     response: void;
   };
@@ -164,6 +164,10 @@ export interface MessageMap {
     request: { contractTermsHash: string; merchantSig: string };
     response: void;
   };
+  "check-upgrade": {
+    request: { };
+    response: void;
+  };
 }
 
 /**
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index 831147f1..f1f0353a 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -36,6 +36,7 @@ import {
 } from "../../types";
 
 import { abbrev, renderAmount } from "../renderHtml";
+import * as wxApi from "../wxApi";
 
 import * as React from "react";
 import * as ReactDOM from "react-dom";
@@ -484,7 +485,7 @@ function reload() {
 function confirmReset() {
   if (confirm("Do you want to IRREVOCABLY DESTROY everything inside your" +
               " wallet and LOSE ALL YOUR COINS?")) {
-    chrome.runtime.sendMessage({type: "reset"});
+    wxApi.resetDb();
     window.close();
   }
 }
diff --git a/src/webex/pages/reset-required.html 
b/src/webex/pages/reset-required.html
new file mode 100644
index 00000000..72b176b4
--- /dev/null
+++ b/src/webex/pages/reset-required.html
@@ -0,0 +1,30 @@
+<!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="../style/wallet.css">
+  <link rel="stylesheet" type="text/css" href="../style/pure.css">
+
+  <script src="/dist/page-common-bundle.js"></script>
+  <script src="/dist/reset-required-bundle.js"></script>
+
+  <style>
+    body {
+      font-size: 100%;
+      overflow-y: scroll;
+    }
+  </style>
+
+</head>
+
+<body>
+  <section id="main">
+    <div id="container"></div>
+  </section>
+</body>
+
+</html>
diff --git a/src/webex/pages/reset-required.tsx 
b/src/webex/pages/reset-required.tsx
new file mode 100644
index 00000000..90ea51ab
--- /dev/null
+++ b/src/webex/pages/reset-required.tsx
@@ -0,0 +1,73 @@
+/*
+ This file is part of TALER
+ (C) 2017 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+
+/**
+ * Page to inform the user when a database reset is required.
+ *
+ * @author Florian Dold
+ */
+
+import * as React from "react";
+import * as ReactDOM from "react-dom";
+
+import * as wxApi from "../wxApi";
+
+class State {
+  checked: boolean;
+  resetRequired: boolean;
+}
+
+
+class ResetNotification extends React.Component<any, State> {
+  constructor(props: any) {
+    super(props);
+    this.state = {checked: false, resetRequired: true};
+    setInterval(() => this.update(), 500);
+  }
+  async update() {
+    const res = await wxApi.checkUpgrade();
+    this.setState({resetRequired: res.dbResetRequired});
+  }
+  render() {
+    if (this.state.resetRequired) {
+      return (
+        <div>
+          <h1>Manual Reset Reqired</h1>
+          <p>The wallet's database in your browser is incompatible with the 
currently installed wallet.  Please reset manually.</p>
+          <p>Once the database format has stabilized, we will provide 
automatic upgrades.</p>
+          <input id="check" type="checkbox" checked={this.state.checked} 
onChange={(e) => this.setState({checked: e.target.checked})} />{" "}
+          <label htmlFor="check">
+            I understand that I will lose all my data
+          </label>
+          <br />
+          <button className="pure-button" disabled={!this.state.checked} 
onClick={() => wxApi.resetDb()}>Reset</button>
+        </div>
+      );
+    }
+    return (
+      <div>
+        <h1>Everything is fine!</h1>
+        A reset is not required anymore, you can close this page.
+      </div>
+    );
+  }
+}
+
+
+document.addEventListener("DOMContentLoaded", () => {
+  ReactDOM.render(<ResetNotification />, document.getElementById( 
"container")!);
+});
diff --git a/src/webex/style/wallet.css b/src/webex/style/wallet.css
index 7bfb99e6..5773eb39 100644
--- a/src/webex/style/wallet.css
+++ b/src/webex/style/wallet.css
@@ -79,10 +79,6 @@ label {
   padding-right: 1em;
 }
 
-label::after {
-  content: ":";
-}
-
 input.url {
   width: 25em;
 }
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 1968b657..a064b413 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -39,6 +39,27 @@ import {
 import { MessageType, MessageMap } from "./messages";
 
 
+/**
+ * Response with information about available version upgrades.
+ */
+export interface UpgradeResponse {
+  /**
+   * Is a reset required because of a new DB version
+   * that can't be atomatically upgraded?
+   */
+  dbResetRequired: boolean;
+
+  /**
+   * Current database version.
+   */
+  currentDbVersion: string;
+
+  /**
+   * Old db version (if applicable).
+   */
+  oldDbVersion: string;
+}
+
 
 async function callBackend<T extends MessageType>(type: T, detail: 
MessageMap[T]["request"]): Promise<any> {
   return new Promise<any>((resolve, reject) => {
@@ -254,3 +275,17 @@ export function getTabCookie(contractTermsHash: string, 
merchantSig: string): Pr
 export function generateNonce(): Promise<string> {
   return callBackend("generate-nonce", { });
 }
+
+/**
+ * Check upgrade information
+ */
+export function checkUpgrade(): Promise<UpgradeResponse> {
+  return callBackend("check-upgrade", { });
+}
+
+/**
+ * Reset database
+ */
+export function resetDb(): Promise<void> {
+  return callBackend("reset-db", { });
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 35fa0b57..0bd4a211 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -44,6 +44,7 @@ import {
 
 import { ChromeBadge } from "./chromeBadge";
 import { MessageType } from "./messages";
+import * as wxApi from "./wxApi";
 
 import URI = require("urijs");
 import Port = chrome.runtime.Port;
@@ -60,23 +61,34 @@ const DB_NAME = "taler";
  */
 const DB_VERSION = 18;
 
-function handleMessage(db: IDBDatabase,
-                       wallet: Wallet,
-                       sender: MessageSender,
+const NeedsWallet = Symbol("NeedsWallet");
+
+function handleMessage(sender: MessageSender,
                        type: MessageType, detail: any): any {
   function assertNotFound(t: never): never {
     console.error(`Request type ${t as string} unknown`);
     console.error(`Request detail was ${detail}`);
     return { error: "request unknown", requestType: type } as never;
   }
+  function needsWallet(): Wallet {
+    if (!currentWallet) {
+      throw NeedsWallet;
+    }
+    return currentWallet;
+  }
   switch (type) {
-    case "balances":
-      return wallet.getBalances();
-    case "dump-db":
+    case "balances": {
+      return needsWallet().getBalances();
+    }
+    case "dump-db": {
+      const db = needsWallet().db;
       return exportDb(db);
-    case "import-db":
+    }
+    case "import-db": {
+      const db = needsWallet().db;
       return importDb(db, detail.dump);
-    case "get-tab-cookie":
+    }
+    case "get-tab-cookie": {
       if (!sender || !sender.tab || !sender.tab.id) {
         return Promise.resolve();
       }
@@ -84,10 +96,13 @@ function handleMessage(db: IDBDatabase,
       const info: any = paymentRequestCookies[id] as any;
       delete paymentRequestCookies[id];
       return Promise.resolve(info);
-    case "ping":
+    }
+    case "ping": {
       return Promise.resolve();
-    case "reset":
-      if (db) {
+    }
+    case "reset-db": {
+      if (currentWallet) {
+        const db = currentWallet.db;
         const tx = db.transaction(Array.from(db.objectStoreNames), 
"readwrite");
         // tslint:disable-next-line:prefer-for-of
         for (let i = 0; i < db.objectStoreNames.length; i++) {
@@ -97,34 +112,42 @@ function handleMessage(db: IDBDatabase,
       deleteDb();
       chrome.browserAction.setBadgeText({ text: "" });
       console.log("reset done");
+      if (!currentWallet) {
+        reinitWallet();
+      }
       return Promise.resolve({});
+    }
     case "create-reserve": {
       const d = {
         amount: detail.amount,
         exchange: detail.exchange,
       };
       const req = CreateReserveRequest.checked(d);
-      return wallet.createReserve(req);
+      return needsWallet().createReserve(req);
     }
-    case "confirm-reserve":
+    case "confirm-reserve": {
       const d = {
         reservePub: detail.reservePub,
       };
       const req = ConfirmReserveRequest.checked(d);
-      return wallet.confirmReserve(req);
-    case "generate-nonce":
-      return wallet.generateNonce();
-    case "confirm-pay":
+      return needsWallet().confirmReserve(req);
+    }
+    case "generate-nonce": {
+      return needsWallet().generateNonce();
+    }
+    case "confirm-pay": {
       if (typeof detail.proposalId !== "number") {
         throw Error("proposalId must be number");
       }
-      return wallet.confirmPay(detail.proposalId);
-    case "check-pay":
+      return needsWallet().confirmPay(detail.proposalId);
+    }
+    case "check-pay": {
       if (typeof detail.proposalId !== "number") {
         throw Error("proposalId must be number");
       }
-      return wallet.checkPay(detail.proposalId);
-    case "query-payment":
+      return needsWallet().checkPay(detail.proposalId);
+    }
+    case "query-payment": {
       if (sender.tab && sender.tab.id) {
         rateLimitCache[sender.tab.id]++;
         if (rateLimitCache[sender.tab.id] > 10) {
@@ -137,99 +160,120 @@ function handleMessage(db: IDBDatabase,
           return Promise.resolve(msg);
         }
       }
-      return wallet.queryPayment(detail.url);
-    case "exchange-info":
+      return needsWallet().queryPayment(detail.url);
+    }
+    case "exchange-info": {
       if (!detail.baseUrl) {
         return Promise.resolve({ error: "bad url" });
       }
-      return wallet.updateExchangeFromUrl(detail.baseUrl);
-    case "currency-info":
+      return needsWallet().updateExchangeFromUrl(detail.baseUrl);
+    }
+    case "currency-info": {
       if (!detail.name) {
         return Promise.resolve({ error: "name missing" });
       }
-      return wallet.getCurrencyRecord(detail.name);
-    case "hash-contract":
+      return needsWallet().getCurrencyRecord(detail.name);
+    }
+    case "hash-contract": {
       if (!detail.contract) {
         return Promise.resolve({ error: "contract missing" });
       }
-      return wallet.hashContract(detail.contract).then((hash) => {
+      return needsWallet().hashContract(detail.contract).then((hash) => {
         return hash;
       });
-    case "put-history-entry":
+    }
+    case "put-history-entry": {
       if (!detail.historyEntry) {
         return Promise.resolve({ error: "historyEntry missing" });
       }
-      return wallet.putHistory(detail.historyEntry);
-    case "save-proposal":
+      return needsWallet().putHistory(detail.historyEntry);
+    }
+    case "save-proposal": {
       console.log("handling save-proposal", detail);
       const checkedRecord = ProposalRecord.checked({
         contractTerms: detail.data,
         contractTermsHash: detail.hash,
         merchantSig: detail.sig,
       });
-      return wallet.saveProposal(checkedRecord);
-    case "reserve-creation-info":
+      return needsWallet().saveProposal(checkedRecord);
+    }
+    case "reserve-creation-info": {
       if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
         return Promise.resolve({ error: "bad url" });
       }
       const amount = AmountJson.checked(detail.amount);
-      return wallet.getReserveCreationInfo(detail.baseUrl, amount);
-    case "get-history":
+      return needsWallet().getReserveCreationInfo(detail.baseUrl, amount);
+    }
+    case "get-history": {
       // TODO: limit history length
-      return wallet.getHistory();
-    case "get-proposal":
-      return wallet.getProposal(detail.proposalId);
-    case "get-exchanges":
-      return wallet.getExchanges();
-    case "get-currencies":
-      return wallet.getCurrencies();
-    case "update-currency":
-      return wallet.updateCurrency(detail.currencyRecord);
-    case "get-reserves":
+      return needsWallet().getHistory();
+    }
+    case "get-proposal": {
+      return needsWallet().getProposal(detail.proposalId);
+    }
+    case "get-exchanges": {
+      return needsWallet().getExchanges();
+    }
+    case "get-currencies": {
+      return needsWallet().getCurrencies();
+    }
+    case "update-currency": {
+      return needsWallet().updateCurrency(detail.currencyRecord);
+    }
+    case "get-reserves": {
       if (typeof detail.exchangeBaseUrl !== "string") {
         return Promise.reject(Error("exchangeBaseUrl missing"));
       }
-      return wallet.getReserves(detail.exchangeBaseUrl);
-    case "get-payback-reserves":
-      return wallet.getPaybackReserves();
-    case "withdraw-payback-reserve":
+      return needsWallet().getReserves(detail.exchangeBaseUrl);
+    }
+    case "get-payback-reserves": {
+      return needsWallet().getPaybackReserves();
+    }
+    case "withdraw-payback-reserve": {
       if (typeof detail.reservePub !== "string") {
         return Promise.reject(Error("reservePub missing"));
       }
-      return wallet.withdrawPaybackReserve(detail.reservePub);
-    case "get-coins":
+      return needsWallet().withdrawPaybackReserve(detail.reservePub);
+    }
+    case "get-coins": {
       if (typeof detail.exchangeBaseUrl !== "string") {
         return Promise.reject(Error("exchangBaseUrl missing"));
       }
-      return wallet.getCoins(detail.exchangeBaseUrl);
-    case "get-precoins":
+      return needsWallet().getCoins(detail.exchangeBaseUrl);
+    }
+    case "get-precoins": {
       if (typeof detail.exchangeBaseUrl !== "string") {
         return Promise.reject(Error("exchangBaseUrl missing"));
       }
-      return wallet.getPreCoins(detail.exchangeBaseUrl);
-    case "get-denoms":
+      return needsWallet().getPreCoins(detail.exchangeBaseUrl); 
+    }
+    case "get-denoms": {
       if (typeof detail.exchangeBaseUrl !== "string") {
         return Promise.reject(Error("exchangBaseUrl missing"));
       }
-      return wallet.getDenoms(detail.exchangeBaseUrl);
-    case "refresh-coin":
+      return needsWallet().getDenoms(detail.exchangeBaseUrl);
+    }
+    case "refresh-coin": {
       if (typeof detail.coinPub !== "string") {
         return Promise.reject(Error("coinPub missing"));
       }
-      return wallet.refresh(detail.coinPub);
-    case "payback-coin":
+      return needsWallet().refresh(detail.coinPub);
+    }
+    case "payback-coin": {
       if (typeof detail.coinPub !== "string") {
         return Promise.reject(Error("coinPub missing"));
       }
-      return wallet.payback(detail.coinPub);
-    case "payment-failed":
+      return needsWallet().payback(detail.coinPub);
+    }
+    case "payment-failed": {
       // For now we just update exchanges (maybe the exchange did something
       // wrong and the keys were messed up).
       // FIXME: in the future we should look at what actually went wrong.
       console.error("payment reported as failed");
-      wallet.updateExchanges();
+      needsWallet().updateExchanges();
       return Promise.resolve();
-    case "payment-succeeded":
+    }
+    case "payment-succeeded": {
       const contractTermsHash = detail.contractTermsHash;
       const merchantSig = detail.merchantSig;
       if (!contractTermsHash) {
@@ -238,7 +282,20 @@ function handleMessage(db: IDBDatabase,
       if (!merchantSig) {
         return Promise.reject(Error("merchantSig missing"));
       }
-      return wallet.paymentSucceeded(contractTermsHash, merchantSig);
+      return needsWallet().paymentSucceeded(contractTermsHash, merchantSig);
+    }
+    case "check-upgrade": {
+      let dbResetRequired = false;
+      if (!currentWallet) {
+        dbResetRequired = true;
+      }
+      const resp: wxApi.UpgradeResponse = {
+        dbResetRequired,
+        currentDbVersion: DB_VERSION.toString(),
+        oldDbVersion: (oldDbVersion || "unknown").toString(),
+      }
+      return resp;
+    }
     default:
       // Exhaustiveness check.
       // See https://www.typescriptlang.org/docs/handbook/advanced-types.html
@@ -246,9 +303,9 @@ function handleMessage(db: IDBDatabase,
   }
 }
 
-async function dispatch(wallet: Wallet, req: any, sender: any, sendResponse: 
any): Promise<void> {
+async function dispatch(req: any, sender: any, sendResponse: any): 
Promise<void> {
   try {
-    const p = handleMessage(wallet.db, wallet, sender, req.type, req.detail);
+    const p = handleMessage(sender, req.type, req.detail);
     const r = await p;
     try {
       sendResponse(r);
@@ -428,6 +485,11 @@ function clearRateLimitCache() {
  */
 let currentWallet: Wallet|undefined;
 
+/**
+ * Last version if an outdated DB, if applicable.
+ */
+let oldDbVersion: number|undefined;
+
 
 async function reinitWallet() {
   if (currentWallet) {
@@ -548,13 +610,7 @@ export async function wxMain() {
   // Handlers for messages coming directly from the content
   // script on the page
   chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    const wallet = currentWallet;
-    if (!wallet) {
-      console.warn("wallet not available while handling message");
-      console.warn("dropped request message was", req);
-      return;
-    }
-    dispatch(wallet, req, sender, sendResponse);
+    dispatch(req, sender, sendResponse);
     return true;
   });
 
@@ -619,8 +675,10 @@ function openTalerDb(): Promise<IDBDatabase> {
           break;
         default:
           if (e.oldVersion !== DB_VERSION) {
-            window.alert("Incompatible wallet dababase version, please reset" +
-                         " db.");
+            oldDbVersion = e.oldVersion;
+            chrome.tabs.create({
+              url: 
chrome.extension.getURL("/src/webex/pages/reset-required.html"),
+            });
             chrome.browserAction.setBadgeText({text: "err"});
             chrome.browserAction.setBadgeBackgroundColor({color: "#F00"});
             throw Error("incompatible DB");
diff --git a/tsconfig.json b/tsconfig.json
index 7bcf7d49..349b5969 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -63,6 +63,7 @@
     "src/webex/pages/logs.tsx",
     "src/webex/pages/payback.tsx",
     "src/webex/pages/popup.tsx",
+    "src/webex/pages/reset-required.tsx",
     "src/webex/pages/show-db.ts",
     "src/webex/pages/tree.tsx",
     "src/webex/renderHtml.tsx",
diff --git a/webpack.config.js b/webpack.config.js
index 60311aaa..34342748 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -73,6 +73,7 @@ module.exports = function (env) {
       "show-db": "./src/webex/pages/show-db.ts",
       "tree": "./src/webex/pages/tree.tsx",
       "payback": "./src/webex/pages/payback.tsx",
+      "reset-required": "./src/webex/pages/reset-required.tsx",
     },
     plugins: [
       new webpack.optimize.CommonsChunkPlugin({

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



reply via email to

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