From 52707f87e7faf1ae985c88b6787b688d121b538b Mon Sep 17 00:00:00 2001
From: Florian Dold <florian.dold@gmail.com>
Date: Fri, 22 Apr 2016 18:03:56 +0200
Subject: adapt to protocol changes and improve error handling

---
 pages/confirm-create-reserve.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'pages/confirm-create-reserve.tsx')

diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx
index be84fff2b..1c3bf7cbb 100644
--- a/pages/confirm-create-reserve.tsx
+++ b/pages/confirm-create-reserve.tsx
@@ -121,8 +121,9 @@ class Controller {
           console.log("get exchange info rejected");
           if (e.hasOwnProperty("httpStatus")) {
             this.statusString = `request failed with status ${this.request.status}`;
-          } else {
-            this.statusString = `unknown request error`;
+          } else if (e.hasOwnProperty("errorResponse")) {
+            let resp = e.errorResponse;
+            this.statusString = `error: ${resp.error} (${resp.hint})`;
           }
           m.endComputation();
         });
-- 
cgit v1.2.3


From 942a6227bae7e32db3f92886a47e00dd7c648067 Mon Sep 17 00:00:00 2001
From: Florian Dold <florian.dold@gmail.com>
Date: Sat, 23 Apr 2016 19:34:30 +0200
Subject: wallet style

---
 pages/confirm-contract.html       | 44 +++++++++++++++++++++++++++--------
 pages/confirm-contract.tsx        |  2 +-
 pages/confirm-create-reserve.html | 48 +++++++++++++++++++++++++++++----------
 pages/confirm-create-reserve.tsx  | 11 +++++----
 4 files changed, 79 insertions(+), 26 deletions(-)

(limited to 'pages/confirm-create-reserve.tsx')

diff --git a/pages/confirm-contract.html b/pages/confirm-contract.html
index 6abb65c67..54d02f316 100644
--- a/pages/confirm-contract.html
+++ b/pages/confirm-contract.html
@@ -5,7 +5,6 @@
   <title>Taler Wallet: Confirm Reserve Creation</title>
 
   <link rel="stylesheet" type="text/css" href="../style/lang.css">
-  <link rel="stylesheet" type="text/css" href="../style/wallet.css">
 
   <script src="../lib/vendor/URI.js"></script>
   <script src="../lib/vendor/mithril.js"></script>
@@ -15,18 +14,45 @@
   <script src="../i18n/strings.js"></script>
   <script src="../lib/i18n.js"></script>
   <script src="../lib/module-trampoline.js"></script>
-</head>
 
-<body>
-  <header>
-    <div id="logo"></div>
-    <h1>Payment Confirmation</h1>
-  </header>
+  <style>
+    #main {
+      border: solid 1px black;
+      border-radius: 10px;
+      margin: auto;
+      max-width: 50%;
+      padding: 2em;
+    }
+
+    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;
 
-  <aside class="sidebar" id="left">
-  </aside>
+      font-family:arial,sans-serif;
+      color:#069;
+      text-decoration:underline;
+      cursor:pointer;
+    }
 
+    input.url {
+      width: 25em;
+    }
+  </style>
+</head>
+
+<body>
   <section id="main">
+    <h1>GNU Taler Wallet</h1>
     <article id="contract" class="fade"></article>
   </section>
 </body>
diff --git a/pages/confirm-contract.tsx b/pages/confirm-contract.tsx
index 0c7419c6c..9e15841e9 100644
--- a/pages/confirm-contract.tsx
+++ b/pages/confirm-contract.tsx
@@ -87,7 +87,7 @@ export function main() {
           _.map(contract.products,
                 (p: any) => m("li",
                               `${p.description}: ${prettyAmount(p.price)}`))),
-        m("button.confirm-pay", {onclick: doPayment}, i18n`Confirm Payment`),
+        m("button.accept", {onclick: doPayment}, i18n`Confirm Payment`),
         m("p", error ? error : []),
         m(Details, contract)
       ];
diff --git a/pages/confirm-create-reserve.html b/pages/confirm-create-reserve.html
index ab0a7c954..da87faf7f 100644
--- a/pages/confirm-create-reserve.html
+++ b/pages/confirm-create-reserve.html
@@ -4,8 +4,6 @@
 <head>
   <title>Taler Wallet: Select Taler Provider</title>
 
-  <link rel="stylesheet" type="text/css" href="../style/wallet.css">
-
   <script src="../lib/vendor/URI.js"></script>
   <script src="../lib/vendor/mithril.js"></script>
   <script src="../lib/vendor/system-csp-production.src.js"></script>
@@ -13,21 +11,47 @@
   <script src="../i18n/strings.js"></script>
   <script src="../lib/i18n.js"></script>
   <script src="../lib/module-trampoline.js"></script>
-</head>
 
-<body>
-  <header>
-    <div id="logo"></div>
-    <h1>Select Taler Provider</h1>
-  </header>
+  <style>
+    #main {
+      border: solid 1px black;
+      border-radius: 10px;
+      margin: auto;
+      max-width: 50%;
+      padding: 2em;
+    }
+
+    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;
 
-  <aside class="sidebar" id="left">
-  </aside>
+      font-family:arial,sans-serif;
+      color:#069;
+      text-decoration:underline;
+      cursor:pointer;
+    }
 
+    input.url {
+      width: 25em;
+    }
+
+  </style>
+</head>
+
+<body>
   <section id="main">
-    <article>
+    <h1>GNU Taler Wallet</h1>
       <div class="fade" id="exchange-selection"></div>
-    </article>
   </section>
 </body>
 
diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx
index 1c3bf7cbb..4b4e19363 100644
--- a/pages/confirm-create-reserve.tsx
+++ b/pages/confirm-create-reserve.tsx
@@ -194,8 +194,9 @@ function view(ctrl: Controller) {
   let mx = (x, ...args) => controls.push(m(x, ...args));
 
   mx("p",
-     i18n`The bank wants to create a reserve over ${amountToPretty(
-       ctrl.amount)}.`);
+     i18n.parts`You are about to withdraw ${m("strong", amountToPretty(
+       ctrl.amount))} from your bank account into your wallet.`);
+
   mx("input",
      {
        className: "url",
@@ -205,14 +206,16 @@ function view(ctrl: Controller) {
        oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
      });
 
-  mx("button", {
+  mx("br");
+
+  mx("button.accept", {
        onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo,
                                           ctrl.url(),
                                           ctrl.amount,
                                           ctrl.callbackUrl),
        disabled: !ctrl.isValidExchange
      },
-     "Confirm exchange selection");
+     "Accept fees and withdraw");
 
   if (ctrl.statusString) {
     mx("p", ctrl.statusString);
-- 
cgit v1.2.3


From d0a4f9f934cea5bbb3da90ea13ccf91d9fb1c933 Mon Sep 17 00:00:00 2001
From: Florian Dold <florian.dold@gmail.com>
Date: Wed, 27 Apr 2016 06:03:04 +0200
Subject: UI fixes

---
 lib/wallet/wallet.ts              | 22 ++++++++++++++++++++++
 lib/wallet/wxMessaging.ts         | 18 ++++++++++++++++++
 pages/confirm-contract.html       | 20 ++++++++++++++++++++
 pages/confirm-contract.tsx        | 30 ++++++++++++++++++++++++++++--
 pages/confirm-create-reserve.html | 11 +++++++++++
 pages/confirm-create-reserve.tsx  | 18 +++++++++++++++---
 6 files changed, 114 insertions(+), 5 deletions(-)

(limited to 'pages/confirm-create-reserve.tsx')

diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts
index dde7a8248..3c9f3ffed 100644
--- a/lib/wallet/wallet.ts
+++ b/lib/wallet/wallet.ts
@@ -521,6 +521,28 @@ export class Wallet {
   }
 
 
+  /**
+   * Add a contract to the wallet and sign coins,
+   * but do not send them yet.
+   */
+  checkPay(offer: Offer): Promise<any> {
+    console.log("executing checkPay");
+    return Promise.resolve().then(() => {
+      return this.getPossibleExchangeCoins(offer.contract.amount,
+                                           offer.contract.max_fee,
+                                           offer.contract.exchanges)
+    }).then((mcs) => {
+      if (Object.keys(mcs).length == 0) {
+        console.log("not confirming payment, insufficient coins");
+        return {
+          error: "coins-insufficient",
+        };
+      }
+      return {};
+    });
+  }
+
+
   /**
    * Retrieve all necessary information for looking up the contract
    * with the given hash.
diff --git a/lib/wallet/wxMessaging.ts b/lib/wallet/wxMessaging.ts
index 64b16de8d..164342f4e 100644
--- a/lib/wallet/wxMessaging.ts
+++ b/lib/wallet/wxMessaging.ts
@@ -99,6 +99,24 @@ function makeHandlers(db: IDBDatabase,
 
       return wallet.confirmPay(offer);
     },
+    ["check-pay"]: function(detail, sender) {
+      let offer;
+      try {
+        offer = Offer.checked(detail.offer);
+      } catch (e) {
+        if (e instanceof Checkable.SchemaError) {
+          console.error("schema error:", e.message);
+          return Promise.resolve({
+                                   error: "invalid contract",
+                                   hint: e.message,
+                                   detail: detail
+                                 });
+        } else {
+          throw e;
+        }
+      }
+      return wallet.checkPay(offer);
+    },
     ["execute-payment"]: function(detail, sender) {
       return wallet.executePayment(detail.H_contract);
     },
diff --git a/pages/confirm-contract.html b/pages/confirm-contract.html
index 54d02f316..ec7eab8c1 100644
--- a/pages/confirm-contract.html
+++ b/pages/confirm-contract.html
@@ -47,6 +47,26 @@
     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>
 
diff --git a/pages/confirm-contract.tsx b/pages/confirm-contract.tsx
index 9e15841e9..2e055d4f1 100644
--- a/pages/confirm-contract.tsx
+++ b/pages/confirm-contract.tsx
@@ -72,6 +72,7 @@ export function main() {
   console.dir(offer);
   let contract = offer.contract;
   let error = null;
+  let payDisabled = true;
 
   var Contract = {
     view(ctrl) {
@@ -87,8 +88,8 @@ export function main() {
           _.map(contract.products,
                 (p: any) => m("li",
                               `${p.description}: ${prettyAmount(p.price)}`))),
-        m("button.accept", {onclick: doPayment}, i18n`Confirm Payment`),
-        m("p", error ? error : []),
+        m("button.accept", {onclick: doPayment, disabled: payDisabled}, i18n`Confirm Payment`),
+        (error ? m("p.errorbox", error) : []),
         m(Details, contract)
       ];
     }
@@ -96,6 +97,31 @@ export function main() {
 
   m.mount(document.getElementById("contract"), Contract);
 
+  function checkPayment() {
+    chrome.runtime.sendMessage({type: 'check-pay', detail: {offer}}, (resp) => {
+      if (resp.error) {
+        console.log("check-pay error", JSON.stringify(resp));
+        switch (resp.error) {
+          case "coins-insufficient":
+            error = "You do not have enough coins of the requested currency.";
+            break;
+          default:
+            error = `Error: ${resp.error}`;
+            break;
+        }
+        payDisabled = true;
+      } else {
+        payDisabled = false;
+        error = null;
+      }
+      m.redraw();
+      window.setTimeout(checkPayment, 300);
+    });
+  }
+
+  checkPayment();
+
+
   function doPayment() {
     let d = {offer};
     chrome.runtime.sendMessage({type: 'confirm-pay', detail: d}, (resp) => {
diff --git a/pages/confirm-create-reserve.html b/pages/confirm-create-reserve.html
index da87faf7f..32a29b9f7 100644
--- a/pages/confirm-create-reserve.html
+++ b/pages/confirm-create-reserve.html
@@ -45,6 +45,17 @@
       width: 25em;
     }
 
+    table {
+      border-collapse: collapse;
+    }
+
+    td {
+      border-left: 1px solid black;
+      border-right: 1px solid black;
+      text-align: center;
+      padding: 0.3em;
+    }
+
   </style>
 </head>
 
diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx
index 4b4e19363..2c959429f 100644
--- a/pages/confirm-create-reserve.tsx
+++ b/pages/confirm-create-reserve.tsx
@@ -250,9 +250,21 @@ function view(ctrl: Controller) {
 function renderReserveCreationDetails(rci: ReserveCreationInfo) {
   let denoms = rci.selectedDenoms;
 
+  let countByPub = {};
+  let uniq = [];
+
+  denoms.forEach((x: Denomination) => {
+    let c = countByPub[x.denom_pub] || 0;
+    if (c == 0) {
+      uniq.push(x);
+    }
+    c += 1;
+    countByPub[x.denom_pub] = c;
+  });
+
   function row(denom: Denomination) {
     return m("tr", [
-      m("td", denom.pub_hash.substr(0, 5) + "..."),
+      m("td", countByPub[denom.denom_pub] + "x"),
       m("td", amountToPretty(denom.value)),
       m("td", amountToPretty(denom.fee_withdraw)),
       m("td", amountToPretty(denom.fee_refresh)),
@@ -267,13 +279,13 @@ function renderReserveCreationDetails(rci: ReserveCreationInfo) {
     m("p", `Overhead: ${overheadStr}`),
     m("table", [
       m("tr", [
-        m("th", "Key Hash"),
+        m("th", "Count"),
         m("th", "Value"),
         m("th", "Withdraw Fee"),
         m("th", "Refresh Fee"),
         m("th", "Deposit Fee"),
       ]),
-      denoms.map(row)
+      uniq.map(row)
     ])
   ];
 }
-- 
cgit v1.2.3


From 2321614c48f91bc8a95b122c7aca33bd4fb307c1 Mon Sep 17 00:00:00 2001
From: Florian Dold <florian.dold@gmail.com>
Date: Wed, 27 Apr 2016 06:50:38 +0200
Subject: simple view

---
 pages/confirm-create-reserve.html |   5 ++
 pages/confirm-create-reserve.tsx  | 104 ++++++++++++++++++++++++++++++--------
 2 files changed, 89 insertions(+), 20 deletions(-)

(limited to 'pages/confirm-create-reserve.tsx')

diff --git a/pages/confirm-create-reserve.html b/pages/confirm-create-reserve.html
index 32a29b9f7..1612340e8 100644
--- a/pages/confirm-create-reserve.html
+++ b/pages/confirm-create-reserve.html
@@ -56,6 +56,11 @@
       padding: 0.3em;
     }
 
+    span.spacer {
+      padding-left: 0.5em;
+      padding-right: 0.5em;
+    }
+
   </style>
 </head>
 
diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx
index 2c959429f..f490451ca 100644
--- a/pages/confirm-create-reserve.tsx
+++ b/pages/confirm-create-reserve.tsx
@@ -76,31 +76,38 @@ class Controller {
   callbackUrl: string;
   wtTypes: string[];
   detailCollapsed = m.prop<boolean>(true);
+  suggestedExchangeUrl: string;
+  complexViewRequested = false;
+  urlOkay = false;
 
-  constructor(initialExchangeUrl: string,
+  constructor(suggestedExchangeUrl: string,
               amount: AmountJson,
               callbackUrl: string,
               wt_types: string[]) {
     console.log("creating main controller");
+    this.suggestedExchangeUrl = suggestedExchangeUrl;
     this.amount = amount;
     this.callbackUrl = callbackUrl;
     this.wtTypes = wt_types;
     this.timer = new DelayTimer(800, () => this.update());
-    this.url(initialExchangeUrl);
+    this.url(suggestedExchangeUrl);
     this.update();
   }
 
   private update() {
     this.timer.stop();
     const doUpdate = () => {
+      this.reserveCreationInfo = null;
       if (!this.url()) {
-        this.statusString = i18n`Please enter a URL`;
+        this.statusString = i18n`Error: URL is empty`;
+        m.redraw(true);
         return;
       }
       this.statusString = null;
       let parsedUrl = URI(this.url());
       if (parsedUrl.is("relative")) {
-        this.statusString = i18n`The URL you've entered is not valid (must be absolute)`;
+        this.statusString = i18n`Error: URL may not be relative`;
+        m.redraw(true);
         return;
       }
 
@@ -114,16 +121,15 @@ class Controller {
           this.isValidExchange = true;
           this.reserveCreationInfo = r;
           console.dir(r);
-          this.statusString = "The exchange base URL is valid!";
           m.endComputation();
         })
         .catch((e) => {
           console.log("get exchange info rejected");
           if (e.hasOwnProperty("httpStatus")) {
-            this.statusString = `request failed with status ${this.request.status}`;
+            this.statusString = `Error: request failed with status ${this.request.status}`;
           } else if (e.hasOwnProperty("errorResponse")) {
             let resp = e.errorResponse;
-            this.statusString = `error: ${resp.error} (${resp.hint})`;
+            this.statusString = `Error: ${resp.error} (${resp.hint})`;
           }
           m.endComputation();
         });
@@ -131,7 +137,7 @@ class Controller {
 
     doUpdate();
 
-    console.log("got update");
+    console.log("got update", this.url());
   }
 
   reset() {
@@ -188,8 +194,7 @@ class Controller {
   }
 }
 
-
-function view(ctrl: Controller) {
+function view(ctrl: Controller): any {
   let controls = [];
   let mx = (x, ...args) => controls.push(m(x, ...args));
 
@@ -197,16 +202,55 @@ function view(ctrl: Controller) {
      i18n.parts`You are about to withdraw ${m("strong", amountToPretty(
        ctrl.amount))} from your bank account into your wallet.`);
 
-  mx("input",
-     {
-       className: "url",
-       type: "text",
-       spellcheck: false,
-       value: ctrl.url(),
-       oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
-     });
+  if (ctrl.complexViewRequested || !ctrl.suggestedExchangeUrl) {
+    return controls.concat(viewComplex(ctrl));
+  }
 
-  mx("br");
+  return controls.concat(viewSimple(ctrl));
+}
+
+function viewSimple(ctrl: Controller) {
+  let controls = [];
+  let mx = (x, ...args) => controls.push(m(x, ...args));
+
+  if (ctrl.statusString) {
+    mx("p", "Error: ", ctrl.statusString);
+    mx("button.linky", {
+      onclick: () => {
+        ctrl.complexViewRequested = true;
+      }
+    }, "advanced options");
+  }
+  else if (ctrl.reserveCreationInfo) {
+    mx("button.accept", {
+         onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo,
+                                            ctrl.url(),
+                                            ctrl.amount,
+                                            ctrl.callbackUrl),
+         disabled: !ctrl.isValidExchange
+       },
+       "Accept fees and withdraw");
+    mx("span.spacer");
+    mx("button.linky", {
+      onclick: () => {
+        ctrl.complexViewRequested = true;
+      }
+    }, "advanced options");
+    let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
+                                ctrl.reserveCreationInfo.withdrawFee).amount;
+    mx("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
+  } else {
+    mx("p", "Please wait ...");
+  }
+
+
+  return controls;
+}
+
+
+function viewComplex(ctrl: Controller) {
+  let controls = [];
+  let mx = (x, ...args) => controls.push(m(x, ...args));
 
   mx("button.accept", {
        onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo,
@@ -216,10 +260,30 @@ function view(ctrl: Controller) {
        disabled: !ctrl.isValidExchange
      },
      "Accept fees and withdraw");
+  mx("span.spacer");
+  mx("button.linky", {
+    onclick: () => {
+      ctrl.complexViewRequested = false;
+    }
+  }, "back to simple view");
+
+  mx("br");
+
+
+  mx("input",
+     {
+       className: "url",
+       type: "text",
+       spellcheck: false,
+       value: ctrl.url(),
+       oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
+     });
+
+  mx("br");
 
   if (ctrl.statusString) {
     mx("p", ctrl.statusString);
-  } else {
+  } else if (!ctrl.reserveCreationInfo) {
     mx("p", "Checking URL, please wait ...");
   }
 
-- 
cgit v1.2.3

