diff --git a/extension/lib/wallet/emscriptif.ts b/extension/lib/wallet/emscriptif.ts
index 16c883451..b11d845f0 100644
--- a/extension/lib/wallet/emscriptif.ts
+++ b/extension/lib/wallet/emscriptif.ts
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, If not, see
*/
-import {AmountJson} from "./wallet";
+import {AmountJson} from "./types";
import * as EmscWrapper from "../emscripten/emsc";
/**
diff --git a/extension/lib/web-common.ts b/extension/lib/wallet/helpers.ts
similarity index 57%
rename from extension/lib/web-common.ts
rename to extension/lib/wallet/helpers.ts
index 79ff4b13e..99913e558 100644
--- a/extension/lib/web-common.ts
+++ b/extension/lib/wallet/helpers.ts
@@ -14,7 +14,13 @@
TALER; see the file COPYING. If not, If not, see
*/
-import {AmountJson} from "./wallet/wallet";
+
+/**
+ * Smaller helper functions that do not depend
+ * on the emscripten machinery.
+ */
+
+import {AmountJson} from "./types";
export function substituteFulfillmentUrl(url: string, vars) {
url = url.replace("${H_contract}", vars.H_contract);
@@ -22,7 +28,38 @@ export function substituteFulfillmentUrl(url: string, vars) {
return url;
}
+
export function amountToPretty(amount: AmountJson): string {
let x = amount.value + amount.fraction / 1e6;
return `${x} ${amount.currency}`;
+}
+
+
+/**
+ * Canonicalize a base url, typically for the mint.
+ *
+ * See http://api.taler.net/wallet.html#general
+ */
+export function canonicalizeBaseUrl(url) {
+ let x = new URI(url);
+ if (!x.protocol()) {
+ x.protocol("https");
+ }
+ x.path(x.path() + "/").normalizePath();
+ x.fragment();
+ x.query();
+ return x.href()
+}
+
+
+export function parsePrettyAmount(pretty: string): AmountJson {
+ const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
+ if (!res) {
+ return null;
+ }
+ return {
+ value: parseInt(res[1], 10),
+ fraction: res[2] ? (parseFloat(`0.${res[2]}`) * 1e-6) : 0,
+ currency: res[3]
+ }
}
\ No newline at end of file
diff --git a/extension/lib/wallet/types.ts b/extension/lib/wallet/types.ts
new file mode 100644
index 000000000..197aed938
--- /dev/null
+++ b/extension/lib/wallet/types.ts
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ */
+
+/**
+ * Common types that are used by Taler.
+ *
+ * Note most types are defined in wallet.ts, types that
+ * are defined in types.ts are intended to be used by components
+ * that do not depend on the whole wallet implementation (which depends on
+ * emscripten).
+ */
+
+import {Checkable} from "./checkable";
+
+@Checkable.Class
+export class AmountJson {
+ @Checkable.Number
+ value: number;
+
+ @Checkable.Number
+ fraction: number;
+
+ @Checkable.String
+ currency: string;
+
+ static checked: (obj: any) => AmountJson;
+}
+
+
+@Checkable.Class
+export class CreateReserveResponse {
+ /**
+ * Mint URL where the bank should create the reserve.
+ * The URL is canonicalized in the response.
+ */
+ @Checkable.String
+ mint: string;
+
+ @Checkable.String
+ reservePub: string;
+
+ static checked: (obj: any) => CreateReserveResponse;
+}
\ No newline at end of file
diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts
index 0a9fbe191..f94e9c87e 100644
--- a/extension/lib/wallet/wallet.ts
+++ b/extension/lib/wallet/wallet.ts
@@ -22,9 +22,11 @@
*/
import * as native from "./emscriptif";
+import {AmountJson, CreateReserveResponse} from "./types";
import {HttpResponse, RequestException} from "./http";
import {Query} from "./query";
import {Checkable} from "./checkable";
+import {canonicalizeBaseUrl} from "./helpers";
"use strict";
@@ -73,21 +75,6 @@ export interface Coin {
}
-@Checkable.Class
-export class AmountJson {
- @Checkable.Number
- value: number;
-
- @Checkable.Number
- fraction: number;
-
- @Checkable.String
- currency: string;
-
- static checked: (obj: any) => AmountJson;
-}
-
-
@Checkable.Class
export class CreateReserveRequest {
/**
@@ -106,22 +93,6 @@ export class CreateReserveRequest {
}
-@Checkable.Class
-export class CreateReserveResponse {
- /**
- * Mint URL where the bank should create the reserve.
- * The URL is canonicalized in the response.
- */
- @Checkable.String
- mint: string;
-
- @Checkable.String
- reservePub: string;
-
- static checked: (obj: any) => CreateReserveResponse;
-}
-
-
@Checkable.Class
export class ConfirmReserveRequest {
/**
@@ -270,34 +241,6 @@ function isWithdrawableDenom(d: Denomination) {
}
-/**
- * See http://api.taler.net/wallet.html#general
- */
-function canonicalizeBaseUrl(url) {
- let x = new URI(url);
- if (!x.protocol()) {
- x.protocol("https");
- }
- x.path(x.path() + "/").normalizePath();
- x.fragment();
- x.query();
- return x.href()
-}
-
-
-function parsePrettyAmount(pretty: string): AmountJson {
- const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
- if (!res) {
- return null;
- }
- return {
- value: parseInt(res[1], 10),
- fraction: res[2] ? (parseFloat(`0.${res[2]}`) * 1e-6) : 0,
- currency: res[3]
- }
-}
-
-
interface HttpRequestLibrary {
req(method: string,
url: string|uri.URI,
@@ -619,7 +562,7 @@ export class Wallet {
reservePub: reserveRecord.reserve_pub,
}
};
-
+
return Query(this.db)
.put("reserves", reserveRecord)
.put("history", historyEntry)
diff --git a/extension/pages/confirm-contract.js b/extension/pages/confirm-contract.js
index d715985b5..5e7d82f98 100644
--- a/extension/pages/confirm-contract.js
+++ b/extension/pages/confirm-contract.js
@@ -13,11 +13,11 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see
*/
-System.register(["../lib/web-common"], function(exports_1, context_1) {
+System.register(["../lib/wallet/helpers"], function(exports_1, context_1) {
///
"use strict";
var __moduleName = context_1 && context_1.id;
- var web_common_1;
+ var helpers_1;
function prettyAmount(amount) {
var v = amount.value + amount.fraction / 1e6;
return v.toFixed(2) + " " + amount.currency;
@@ -55,15 +55,15 @@ System.register(["../lib/web-common"], function(exports_1, context_1) {
}
var c = d.offer.contract;
console.log("contract", c);
- document.location.href = web_common_1.substituteFulfillmentUrl(c.fulfillment_url, offer);
+ document.location.href = helpers_1.substituteFulfillmentUrl(c.fulfillment_url, offer);
});
}
}
exports_1("main", main);
return {
setters:[
- function (web_common_1_1) {
- web_common_1 = web_common_1_1;
+ function (helpers_1_1) {
+ helpers_1 = helpers_1_1;
}],
execute: function() {
}
diff --git a/extension/pages/confirm-contract.tsx b/extension/pages/confirm-contract.tsx
index 35d050c19..055490175 100644
--- a/extension/pages/confirm-contract.tsx
+++ b/extension/pages/confirm-contract.tsx
@@ -17,7 +17,7 @@
///
"use strict";
-import {substituteFulfillmentUrl} from "../lib/web-common";
+import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
declare var m: any;
diff --git a/extension/pages/confirm-create-reserve.html b/extension/pages/confirm-create-reserve.html
index 7af54a828..522efc872 100644
--- a/extension/pages/confirm-create-reserve.html
+++ b/extension/pages/confirm-create-reserve.html
@@ -4,6 +4,8 @@
- You asked to withdraw (loading...) from your
- bank account.
-
-
- Please specify the base URL of the Taler mint you want to use. The Taler
- mint will process the payments, possibly for a fee. The mint underwrites
- electronic coins and will hold matching funds in reserve in its bank
- account. Mints are expected to be regularly audited by a trusted party to
- ensure that they have sufficient reserves to cover all outstanding
- obligations.
-
-
-
-
-
-
-
-
-
+
diff --git a/extension/pages/confirm-create-reserve.js b/extension/pages/confirm-create-reserve.js
index 3ac757249..124abdf22 100644
--- a/extension/pages/confirm-create-reserve.js
+++ b/extension/pages/confirm-create-reserve.js
@@ -13,69 +13,176 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see
*/
-System.register(["../lib/web-common", "../lib/wallet/wallet"], function(exports_1, context_1) {
+System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
- var web_common_1, wallet_1;
+ var helpers_1, types_1;
+ var DelayTimer, Controller;
function main() {
- function updateAmount() {
- var showAmount = document.getElementById("show-amount");
- console.log("Query is " + JSON.stringify(query));
- var amount = wallet_1.AmountJson.checked(JSON.parse(query.amount));
- showAmount.textContent = web_common_1.amountToPretty(amount);
- }
var url = URI(document.location.href);
var query = URI.parseQuery(url.query());
- updateAmount();
- document.getElementById("confirm").addEventListener("click", function (e) {
- var d = {
- mint: document.getElementById('mint-url').value,
- amount: JSON.parse(query.amount)
- };
- if (!d.mint) {
- // FIXME: indicate error instead!
- throw Error("mint missing");
- }
- if (!d.amount) {
- // FIXME: indicate error instead!
- throw Error("amount missing");
- }
- var cb = function (rawResp) {
- if (!rawResp) {
- throw Error("empty response");
- }
- if (!rawResp.error) {
- var resp = wallet_1.CreateReserveResponse.checked(rawResp);
- var q = {
- mint: resp.mint,
- reserve_pub: resp.reservePub,
- amount: query.amount,
- };
- var url_1 = URI(query.callback_url).addQuery(q);
- if (!url_1.is("absolute")) {
- throw Error("callback url is not absolute");
+ var amount = types_1.AmountJson.checked(JSON.parse(query.amount));
+ var callback_url = query.callback_url;
+ var MintSelection = {
+ controller: function () { return new Controller(); },
+ view: function (ctrl) {
+ var controls = [];
+ var mx = function () {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i - 0] = arguments[_i];
}
- document.location.href = url_1.href();
+ return controls.push(m.apply(void 0, args));
+ };
+ mx("p", (_a = ["The bank wants to create a reserve over ", "."], _a.raw = ["The bank wants to create a reserve over ", "."], i18n(_a, helpers_1.amountToPretty(amount))));
+ mx("input.url", {
+ type: "text",
+ spellcheck: false,
+ oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
+ });
+ if (ctrl.isValidMint) {
+ mx("button", {
+ onclick: function () { return ctrl.confirmReserve(ctrl.url, amount, callback_url); }
+ }, "Confirm mint selection");
}
- else {
- document.body.innerHTML =
- "Oops, something went wrong. It looks like the bank could not\n transfer funds to the mint. Please go back to your bank's website\n to check what happened.";
+ if (ctrl.errorString) {
+ mx("p", ctrl.errorString);
}
- };
- chrome.runtime.sendMessage({ type: 'create-reserve', detail: d }, cb);
- });
+ return m("div", controls);
+ var _a;
+ }
+ };
+ m.mount(document.getElementById("mint-selection"), MintSelection);
}
exports_1("main", main);
return {
setters:[
- function (web_common_1_1) {
- web_common_1 = web_common_1_1;
+ function (helpers_1_1) {
+ helpers_1 = helpers_1_1;
},
- function (wallet_1_1) {
- wallet_1 = wallet_1_1;
+ function (types_1_1) {
+ types_1 = types_1_1;
}],
execute: function() {
"use strict";
+ /**
+ * Execute something after a delay, with the possibility
+ * to reset the delay.
+ */
+ DelayTimer = (function () {
+ function DelayTimer(ms, f) {
+ this.timerId = null;
+ this.f = f;
+ this.ms = ms;
+ }
+ DelayTimer.prototype.bump = function () {
+ var _this = this;
+ if (this.timerId !== null) {
+ window.clearTimeout(this.timerId);
+ }
+ var handler = function () {
+ _this.f();
+ };
+ this.timerId = window.setTimeout(handler, this.ms);
+ };
+ return DelayTimer;
+ }());
+ Controller = (function () {
+ function Controller() {
+ var _this = this;
+ this.url = null;
+ this.errorString = null;
+ this.isValidMint = false;
+ this.update();
+ this.timer = new DelayTimer(800, function () { return _this.update(); });
+ }
+ Controller.prototype.update = function () {
+ var _this = this;
+ var doUpdate = function () {
+ if (!_this.url) {
+ _this.errorString = (_a = ["Please enter a URL"], _a.raw = ["Please enter a URL"], i18n(_a));
+ return;
+ }
+ _this.errorString = null;
+ var parsedUrl = URI(_this.url);
+ if (parsedUrl.is("relative")) {
+ _this.errorString = (_b = ["The URL you've entered is not valid (must be absolute)"], _b.raw = ["The URL you've entered is not valid (must be absolute)"], i18n(_b));
+ return;
+ }
+ var keysUrl = URI("/keys").absoluteTo(helpers_1.canonicalizeBaseUrl(_this.url));
+ console.log("requesting keys from '" + keysUrl + "'");
+ _this.request = new XMLHttpRequest();
+ _this.request.onreadystatechange = function () {
+ if (_this.request.readyState == XMLHttpRequest.DONE) {
+ switch (_this.request.status) {
+ case 200:
+ _this.isValidMint = true;
+ break;
+ case 0:
+ _this.errorString = "unknown request error";
+ break;
+ default:
+ _this.errorString = "request failed with status " + _this.request.status;
+ break;
+ }
+ m.redraw();
+ }
+ };
+ _this.request.open("get", keysUrl.href());
+ _this.request.send();
+ var _a, _b;
+ };
+ doUpdate();
+ m.redraw();
+ console.log("got update");
+ };
+ Controller.prototype.reset = function () {
+ this.isValidMint = false;
+ this.errorString = null;
+ if (this.request) {
+ this.request.abort();
+ this.request = null;
+ }
+ m.redraw();
+ };
+ Controller.prototype.confirmReserve = function (mint, amount, callback_url) {
+ var _this = this;
+ var d = { mint: mint, amount: amount };
+ var cb = function (rawResp) {
+ if (!rawResp) {
+ throw Error("empty response");
+ }
+ if (!rawResp.error) {
+ var resp = types_1.CreateReserveResponse.checked(rawResp);
+ var q = {
+ mint: resp.mint,
+ reserve_pub: resp.reservePub,
+ amount_value: amount.value,
+ amount_fraction: amount.fraction,
+ amount_currency: amount.currency,
+ };
+ var url = URI(callback_url).addQuery(q);
+ if (!url.is("absolute")) {
+ throw Error("callback url is not absolute");
+ }
+ console.log("going to", url.href());
+ document.location.href = url.href();
+ }
+ else {
+ _this.reset();
+ _this.errorString = ("Oops, something went wrong." +
+ ("The wallet responded with error status (" + rawResp.error + ")."));
+ }
+ };
+ chrome.runtime.sendMessage({ type: 'create-reserve', detail: d }, cb);
+ };
+ Controller.prototype.onUrlChanged = function (url) {
+ this.reset();
+ this.url = url;
+ this.timer.bump();
+ };
+ return Controller;
+ }());
}
}
});
diff --git a/extension/pages/confirm-create-reserve.tsx b/extension/pages/confirm-create-reserve.tsx
index 6a130eeff..8cb46559b 100644
--- a/extension/pages/confirm-create-reserve.tsx
+++ b/extension/pages/confirm-create-reserve.tsx
@@ -14,40 +14,107 @@
TALER; see the file COPYING. If not, If not, see
*/
-import {amountToPretty} from "../lib/web-common";
-import {AmountJson, CreateReserveResponse} from "../lib/wallet/wallet";
+import {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers";
+import {AmountJson, CreateReserveResponse} from "../lib/wallet/types";
+
"use strict";
+declare var m: any;
-export function main() {
- function updateAmount() {
- let showAmount = document.getElementById("show-amount");
- console.log("Query is " + JSON.stringify(query));
- let amount = AmountJson.checked(JSON.parse(query.amount));
- showAmount.textContent = amountToPretty(amount);
+
+/**
+ * Execute something after a delay, with the possibility
+ * to reset the delay.
+ */
+class DelayTimer {
+ ms: number;
+ f;
+ timerId: number = null;
+
+ constructor(ms: number, f) {
+ this.f = f;
+ this.ms = ms;
}
- let url = URI(document.location.href);
- let query: any = URI.parseQuery(url.query());
+ bump() {
+ if (this.timerId !== null) {
+ window.clearTimeout(this.timerId);
+ }
+ const handler = () => {
+ this.f();
+ };
+ this.timerId = window.setTimeout(handler, this.ms);
+ }
+}
- updateAmount();
- document.getElementById("confirm").addEventListener("click", (e) => {
- const d = {
- mint: (document.getElementById('mint-url') as HTMLInputElement).value,
- amount: JSON.parse(query.amount)
+class Controller {
+ url = null;
+ errorString = null;
+ isValidMint = false;
+ private timer: DelayTimer;
+ private request: XMLHttpRequest;
+
+ constructor() {
+ this.update();
+ this.timer = new DelayTimer(800, () => this.update());
+ }
+
+ update() {
+ const doUpdate = () => {
+ if (!this.url) {
+ this.errorString = i18n`Please enter a URL`;
+ return;
+ }
+ this.errorString = null;
+ let parsedUrl = URI(this.url);
+ if (parsedUrl.is("relative")) {
+ this.errorString = i18n`The URL you've entered is not valid (must be absolute)`;
+ return;
+ }
+
+ const keysUrl = URI("/keys").absoluteTo(canonicalizeBaseUrl(this.url));
+
+ console.log(`requesting keys from '${keysUrl}'`);
+
+ this.request = new XMLHttpRequest();
+ this.request.onreadystatechange = () => {
+ if (this.request.readyState == XMLHttpRequest.DONE) {
+ switch (this.request.status) {
+ case 200:
+ this.isValidMint = true;
+ break;
+ case 0:
+ this.errorString = `unknown request error`;
+ break;
+ default:
+ this.errorString = `request failed with status ${this.request.status}`;
+ break;
+ }
+ m.redraw();
+ }
+ };
+ this.request.open("get", keysUrl.href());
+ this.request.send();
};
- if (!d.mint) {
- // FIXME: indicate error instead!
- throw Error("mint missing");
- }
+ doUpdate();
+ m.redraw();
+ console.log("got update");
+ }
- if (!d.amount) {
- // FIXME: indicate error instead!
- throw Error("amount missing");
+ reset() {
+ this.isValidMint = false;
+ this.errorString = null;
+ if (this.request) {
+ this.request.abort();
+ this.request = null;
}
+ m.redraw();
+ }
+ confirmReserve(mint: string, amount: AmountJson, callback_url: string) {
+ const d = {mint, amount};
const cb = (rawResp) => {
if (!rawResp) {
throw Error("empty response");
@@ -57,20 +124,72 @@ export function main() {
let q = {
mint: resp.mint,
reserve_pub: resp.reservePub,
- amount: query.amount,
+ amount_value: amount.value,
+ amount_fraction: amount.fraction,
+ amount_currency: amount.currency,
};
- let url = URI(query.callback_url).addQuery(q);
+ let url = URI(callback_url).addQuery(q);
if (!url.is("absolute")) {
throw Error("callback url is not absolute");
}
+ console.log("going to", url.href());
document.location.href = url.href();
} else {
- document.body.innerHTML =
- `Oops, something went wrong. It looks like the bank could not
- transfer funds to the mint. Please go back to your bank's website
- to check what happened.`;
+ this.reset();
+ this.errorString = (
+ `Oops, something went wrong.` +
+ `The wallet responded with error status (${rawResp.error}).`);
}
};
chrome.runtime.sendMessage({type: 'create-reserve', detail: d}, cb);
- });
+ }
+
+ onUrlChanged(url: string) {
+ this.reset();
+ this.url = url;
+ this.timer.bump();
+ }
+}
+
+
+export function main() {
+ const url = URI(document.location.href);
+ const query: any = URI.parseQuery(url.query());
+ const amount = AmountJson.checked(JSON.parse(query.amount));
+ const callback_url = query.callback_url;
+
+ var MintSelection = {
+ controller: () => new Controller(),
+ view(ctrl: Controller) {
+ let controls = [];
+ let mx = (...args) => controls.push(m(...args));
+
+ mx("p",
+ i18n`The bank wants to create a reserve over ${amountToPretty(
+ amount)}.`);
+ mx("input.url",
+ {
+ type: "text",
+ spellcheck: false,
+ oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
+ });
+
+ if (ctrl.isValidMint) {
+ mx("button", {
+ onclick: () => ctrl.confirmReserve(ctrl.url,
+ amount,
+ callback_url)
+ },
+ "Confirm mint selection");
+ }
+
+ if (ctrl.errorString) {
+ mx("p", ctrl.errorString);
+ }
+
+ return m("div", controls);
+ }
+ };
+
+ m.mount(document.getElementById("mint-selection"), MintSelection);
}
\ No newline at end of file
diff --git a/extension/popup/popup.tsx b/extension/popup/popup.tsx
index ebd77dd2e..97ea438bd 100644
--- a/extension/popup/popup.tsx
+++ b/extension/popup/popup.tsx
@@ -20,7 +20,7 @@
"use strict";
-import {substituteFulfillmentUrl} from "../lib/web-common";
+import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
declare var m: any;
declare var i18n: any;
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index 5c2883da4..69fab261d 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -12,12 +12,13 @@
"files": [
"lib/i18n.ts",
"lib/refs.ts",
- "lib/web-common.ts",
"lib/wallet/checkable.ts",
"lib/wallet/db.ts",
"lib/wallet/emscriptif.ts",
+ "lib/wallet/helpers.ts",
"lib/wallet/http.ts",
"lib/wallet/query.ts",
+ "lib/wallet/types.ts",
"lib/wallet/wallet.ts",
"lib/wallet/wxmessaging.ts",
"background/main.ts",