aboutsummaryrefslogtreecommitdiff
path: root/pages/confirm-create-reserve.tsx
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-02-29 18:03:02 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-02-29 18:03:02 +0100
commitc962e9402123900c53967c14cf809ea10576cdb8 (patch)
treee7df9cfdd6fceae30fb99c8ec6be5e07c8b153a8 /pages/confirm-create-reserve.tsx
parent30ee3320c788129b258ed8b42f4fc63d28431e2f (diff)
restructure
Diffstat (limited to 'pages/confirm-create-reserve.tsx')
-rw-r--r--pages/confirm-create-reserve.tsx305
1 files changed, 305 insertions, 0 deletions
diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx
new file mode 100644
index 000000000..224a3e895
--- /dev/null
+++ b/pages/confirm-create-reserve.tsx
@@ -0,0 +1,305 @@
+/*
+ This file is part of TALER
+ (C) 2015-2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+ */
+
+/// <reference path="../lib/decl/mithril.d.ts" />
+
+import {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers";
+import {AmountJson, CreateReserveResponse} from "../lib/wallet/types";
+import m from "mithril";
+import {IMintInfo} from "../lib/wallet/types";
+import {ReserveCreationInfo, Amounts} from "../lib/wallet/types";
+import MithrilComponent = _mithril.MithrilComponent;
+import {Denomination} from "../lib/wallet/types";
+import {getReserveCreationInfo} from "../lib/wallet/wxApi";
+
+"use strict";
+
+/**
+ * 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;
+ }
+
+ bump() {
+ this.stop();
+ const handler = () => {
+ this.f();
+ };
+ this.timerId = window.setTimeout(handler, this.ms);
+ }
+
+ stop() {
+ if (this.timerId !== null) {
+ window.clearTimeout(this.timerId);
+ }
+ }
+}
+
+
+class Controller {
+ url = m.prop<string>();
+ statusString = null;
+ isValidMint = false;
+ reserveCreationInfo: ReserveCreationInfo = null;
+ private timer: DelayTimer;
+ private request: XMLHttpRequest;
+ amount: AmountJson;
+ callbackUrl: string;
+ detailCollapsed = m.prop<boolean>(true);
+
+ constructor(initialMintUrl: string, amount: AmountJson, callbackUrl: string) {
+ console.log("creating main controller");
+ this.amount = amount;
+ this.callbackUrl = callbackUrl;
+ this.timer = new DelayTimer(800, () => this.update());
+ this.url(initialMintUrl);
+ this.update();
+ }
+
+ private update() {
+ this.timer.stop();
+ const doUpdate = () => {
+ if (!this.url()) {
+ this.statusString = i18n`Please enter a URL`;
+ 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)`;
+ return;
+ }
+
+ m.redraw(true);
+
+ console.log("doing get mint info");
+
+ getReserveCreationInfo(this.url(), this.amount)
+ .then((r: ReserveCreationInfo) => {
+ console.log("get mint info resolved");
+ this.isValidMint = true;
+ this.reserveCreationInfo = r;
+ console.dir(r);
+ this.statusString = "The mint base URL is valid!";
+ m.endComputation();
+ })
+ .catch((e) => {
+ console.log("get mint info rejected");
+ if (e.hasOwnProperty("httpStatus")) {
+ this.statusString = `request failed with status ${this.request.status}`;
+ } else {
+ this.statusString = `unknown request error`;
+ }
+ m.endComputation();
+ });
+ };
+
+ doUpdate();
+
+
+ console.log("got update");
+ }
+
+ reset() {
+ this.isValidMint = false;
+ this.statusString = null;
+ this.reserveCreationInfo = null;
+ if (this.request) {
+ this.request.abort();
+ this.request = null;
+ }
+ }
+
+ confirmReserve(mint: string, amount: AmountJson, callback_url: string) {
+ const d = {mint, amount};
+ const cb = (rawResp) => {
+ if (!rawResp) {
+ throw Error("empty response");
+ }
+ if (!rawResp.error) {
+ const resp = CreateReserveResponse.checked(rawResp);
+ let q = {
+ mint: resp.mint,
+ reserve_pub: resp.reservePub,
+ amount_value: amount.value,
+ amount_fraction: amount.fraction,
+ amount_currency: amount.currency,
+ };
+ 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 {
+ this.reset();
+ this.statusString = (
+ `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();
+ }
+}
+
+
+function view(ctrl: Controller) {
+ let controls = [];
+ let mx = (x, ...args) => controls.push(m(x, ...args));
+
+ mx("p",
+ i18n`The bank wants to create a reserve over ${amountToPretty(
+ ctrl.amount)}.`);
+ mx("input",
+ {
+ className: "url",
+ type: "text",
+ spellcheck: false,
+ value: ctrl.url(),
+ oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
+ });
+
+ mx("button", {
+ onclick: () => ctrl.confirmReserve(ctrl.url(),
+ ctrl.amount,
+ ctrl.callbackUrl),
+ disabled: !ctrl.isValidMint
+ },
+ "Confirm exchange selection");
+
+ if (ctrl.statusString) {
+ mx("p", ctrl.statusString);
+ } else {
+ mx("p", "Checking URL, please wait ...");
+ }
+
+ if (ctrl.reserveCreationInfo) {
+ let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
+ ctrl.reserveCreationInfo.withdrawFee).amount;
+ mx("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
+ if (ctrl.detailCollapsed()) {
+ mx("button.linky", {
+ onclick: () => {
+ ctrl.detailCollapsed(false);
+ }
+ }, "show more details");
+ } else {
+ mx("button.linky", {
+ onclick: () => {
+ ctrl.detailCollapsed(true);
+ }
+ }, "hide details");
+ mx("div", {}, renderReserveCreationDetails(ctrl.reserveCreationInfo))
+ }
+ }
+
+ return m("div", controls);
+}
+
+
+function renderReserveCreationDetails(rci: ReserveCreationInfo) {
+ let denoms = rci.selectedDenoms;
+
+ function row(denom: Denomination) {
+ return m("tr", [
+ m("td", denom.pub_hash.substr(0, 5) + "..."),
+ m("td", amountToPretty(denom.value)),
+ m("td", amountToPretty(denom.fee_withdraw)),
+ m("td", amountToPretty(denom.fee_refresh)),
+ m("td", amountToPretty(denom.fee_deposit)),
+ ]);
+ }
+
+ let withdrawFeeStr = amountToPretty(rci.withdrawFee);
+ let overheadStr = amountToPretty(rci.overhead);
+ return [
+ m("p", `Fee for withdrawal: ${withdrawFeeStr}`),
+ m("p", `Overhead: ${overheadStr}`),
+ m("table", [
+ m("tr", [
+ m("th", "Key Hash"),
+ m("th", "Value"),
+ m("th", "Withdraw Fee"),
+ m("th", "Refresh Fee"),
+ m("th", "Deposit Fee"),
+ ]),
+ denoms.map(row)
+ ])
+ ];
+}
+
+
+interface MintProbeResult {
+ keyInfo?: any;
+}
+
+function probeMint(mintBaseUrl: string): Promise<MintProbeResult> {
+ throw Error("not implemented");
+}
+
+
+function getSuggestedMint(currency: string): Promise<string> {
+ // TODO: make this request go to the wallet backend
+ // Right now, this is a stub.
+ const defaultMint = {
+ "KUDOS": "http://exchange.demo.taler.net",
+ "PUDOS": "http://exchange.test.taler.net",
+ };
+
+ let mint = defaultMint[currency];
+
+ if (!mint) {
+ mint = ""
+ }
+
+ return Promise.resolve(mint);
+}
+
+
+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;
+ const bank_url = query.bank_url;
+
+ getSuggestedMint(amount.currency)
+ .then((suggestedMintUrl) => {
+ const controller = () => new Controller(suggestedMintUrl, amount, callback_url);
+ var MintSelection = {controller, view};
+ m.mount(document.getElementById("mint-selection"), MintSelection);
+ })
+ .catch((e) => {
+ // TODO: provide more context information, maybe factor it out into a
+ // TODO:generic error reporting function or component.
+ document.body.innerText = `Fatal error: "${e.message}".`;
+ console.error(`got backend error "${e.message}"`);
+ });
+}