suggest mint based on currency (stub)

This commit is contained in:
Florian Dold 2016-02-15 11:29:58 +01:00
parent c34a6612ec
commit 526e88695f
12 changed files with 3286 additions and 2399 deletions

View File

@ -44,6 +44,8 @@ var TalerNotify;
var params = { var params = {
amount: JSON.stringify(e.detail.amount), amount: JSON.stringify(e.detail.amount),
callback_url: URI(e.detail.callback_url).absoluteTo(document.location.href), callback_url: URI(e.detail.callback_url).absoluteTo(document.location.href),
bank_url: document.location.href,
suggested_mint: e.detail.suggested_mint,
}; };
var uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html")); var uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
document.location.href = uri.query(params).href(); document.location.href = uri.query(params).href();

View File

@ -53,6 +53,8 @@ namespace TalerNotify {
let params = { let params = {
amount: JSON.stringify(e.detail.amount), amount: JSON.stringify(e.detail.amount),
callback_url: URI(e.detail.callback_url).absoluteTo(document.location.href), callback_url: URI(e.detail.callback_url).absoluteTo(document.location.href),
bank_url: document.location.href,
suggested_mint: e.detail.suggested_mint,
}; };
let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html")); let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
document.location.href = uri.query(params).href(); document.location.href = uri.query(params).href();

View File

@ -3,7 +3,8 @@
/** /**
* This is the module containing all the types/declarations/etc. for Mithril * This is the module containing all the types/declarations/etc. for Mithril
*/ */
declare interface MithrilStatic { declare module _mithril {
interface MithrilStatic {
/** /**
* Creates a virtual element for use with m.render, m.mount, etc. * Creates a virtual element for use with m.render, m.mount, etc.
* *
@ -19,11 +20,13 @@ declare interface MithrilStatic {
* @see m.mount * @see m.mount
* @see m.component * @see m.component
*/ */
<T extends MithrilController>(selector: string, <T extends MithrilController>(
selector: string,
attributes: MithrilAttributes, attributes: MithrilAttributes,
...children: Array<string | ...children: Array<string |
MithrilVirtualElement<T> | MithrilVirtualElement<T> |
MithrilComponent<T>>): MithrilVirtualElement<T>; MithrilComponent<T>>
): MithrilVirtualElement<T>;
/** /**
* Initializes a component for use with m.render, m.mount, etc. * Initializes a component for use with m.render, m.mount, etc.
@ -36,8 +39,10 @@ declare interface MithrilStatic {
* @see m.mount * @see m.mount
* @see m * @see m
*/ */
<T extends MithrilController>(component: MithrilComponent<T>, <T extends MithrilController>(
...args: any[]): MithrilComponent<T>; component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/** /**
* Creates a virtual element for use with m.render, m.mount, etc. * Creates a virtual element for use with m.render, m.mount, etc.
@ -51,10 +56,12 @@ declare interface MithrilStatic {
* @see m.mount * @see m.mount
* @see m.component * @see m.component
*/ */
<T extends MithrilController>(selector: string, <T extends MithrilController>(
selector: string,
...children: Array<string | ...children: Array<string |
MithrilVirtualElement<T> | MithrilVirtualElement<T> |
MithrilComponent<T>>): MithrilVirtualElement<T>; MithrilComponent<T>>
): MithrilVirtualElement<T>;
/** /**
* Initializes a component for use with m.render, m.mount, etc. * Initializes a component for use with m.render, m.mount, etc.
@ -68,8 +75,10 @@ declare interface MithrilStatic {
* @see m.mount * @see m.mount
* @see m.component * @see m.component
*/ */
<T extends MithrilController>(component: MithrilComponent<T>, <T extends MithrilController>(
...args: any[]): MithrilComponent<T>; component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/** /**
* Creates a getter-setter function that wraps a Mithril promise. Useful * Creates a getter-setter function that wraps a Mithril promise. Useful
@ -112,15 +121,32 @@ declare interface MithrilStatic {
* @param callback The handler to use the value from the event. * @param callback The handler to use the value from the event.
* @return A function suitable for listening to an event. * @return A function suitable for listening to an event.
*/ */
withAttr(property: string, withAttr(
property: string,
callback: (value: any) => void, callback: (value: any) => void,
callbackThis: any): (e: Event) => any; callbackThis: any
): (e: Event) => any;
/**
* Returns a event handler that can be bound to an element, firing with
* the specified property.
*
* @param attributeName Name of the element's attribute to bind to.
* @param property The property to bind.
* @return A function suitable for listening to an event.
*/
withAttr<T>(
attributeName: string,
property: MithrilBasicProperty<T>
) : (e: Event) => any;
/** /**
* @deprecated Use m.mount instead * @deprecated Use m.mount instead
*/ */
module<T extends MithrilController>(rootElement: Node, module<T extends MithrilController>(
component: MithrilComponent<T>): T; rootElement: Node,
component: MithrilComponent<T>
): T;
/** /**
* Mounts a component to a base DOM node. * Mounts a component to a base DOM node.
@ -129,8 +155,10 @@ declare interface MithrilStatic {
* @param component The component to mount. * @param component The component to mount.
* @return An instance of the top-level component's controller * @return An instance of the top-level component's controller
*/ */
mount<T extends MithrilController>(rootElement: Node, mount<T extends MithrilController>(
component: MithrilComponent<T>): T; rootElement: Node,
component: MithrilComponent<T>
): T;
/** /**
* Initializes a component for use with m.render, m.mount, etc. * Initializes a component for use with m.render, m.mount, etc.
@ -143,8 +171,10 @@ declare interface MithrilStatic {
* @see m.mount * @see m.mount
* @see m * @see m
*/ */
component<T extends MithrilController>(component: MithrilComponent<T>, component<T extends MithrilController>(
...args: any[]): MithrilComponent<T>; component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/** /**
* Trust this string of HTML. * Trust this string of HTML.
@ -163,9 +193,11 @@ declare interface MithrilStatic {
* @param forceRecreation If true, overwrite the entire tree without * @param forceRecreation If true, overwrite the entire tree without
* diffing against it. * diffing against it.
*/ */
render<T extends MithrilController>(rootElement: Element, render<T extends MithrilController>(
rootElement: Element,
children: MithrilVirtualElement<T>|MithrilVirtualElement<T>[], children: MithrilVirtualElement<T>|MithrilVirtualElement<T>[],
forceRecreation?: boolean): void; forceRecreation?: boolean
): void;
redraw: { redraw: {
/** /**
@ -228,9 +260,11 @@ declare interface MithrilStatic {
* @param defaultRoute The route to start with. * @param defaultRoute The route to start with.
* @param routes A key-value mapping of pathname to controller. * @param routes A key-value mapping of pathname to controller.
*/ */
<T extends MithrilController>(rootElement: Element, <T extends MithrilController>(
rootElement: Element,
defaultRoute: string, defaultRoute: string,
routes: MithrilRoutes): void; routes: MithrilRoutes
): void;
/** /**
* This allows m.route to be used as the `config` attribute for a * This allows m.route to be used as the `config` attribute for a
@ -242,10 +276,12 @@ declare interface MithrilStatic {
* m("a[href='/dashboard/alicesmith']", {config: m.route}); * m("a[href='/dashboard/alicesmith']", {config: m.route});
* ``` * ```
*/ */
<T extends MithrilController>(element: Element, <T extends MithrilController>(
element: Element,
isInitialized: boolean, isInitialized: boolean,
context?: MithrilContext, context?: MithrilContext,
vdom?: MithrilVirtualElement<T>): void; vdom?: MithrilVirtualElement<T>
): void;
/** /**
* Programmatically redirect to another route. * Programmatically redirect to another route.
@ -479,10 +515,12 @@ interface MithrilElementConfig {
* @param context The associated context for this element. * @param context The associated context for this element.
* @param vdom The associated virtual element. * @param vdom The associated virtual element.
*/ */
<T extends MithrilController>(element: Element, <T extends MithrilController>(
element: Element,
isInitialized: boolean, isInitialized: boolean,
context: MithrilContext, context: MithrilContext,
vdom: MithrilVirtualElement<T>): void; vdom: MithrilVirtualElement<T>
): void;
} }
/** /**
@ -536,7 +574,7 @@ interface MithrilController {
* @see MithrilControllerConstructor * @see MithrilControllerConstructor
*/ */
interface MithrilControllerFunction<T extends MithrilController> { interface MithrilControllerFunction<T extends MithrilController> {
(): T; (opts?: any): T;
} }
/** /**
@ -570,7 +608,7 @@ interface MithrilComponent<T extends MithrilController> {
* *
* @see m.component * @see m.component
*/ */
controller?: MithrilControllerFunction<T> | controller: MithrilControllerFunction<T> |
MithrilControllerConstructor<T>; MithrilControllerConstructor<T>;
/** /**
@ -578,7 +616,7 @@ interface MithrilComponent<T extends MithrilController> {
* *
* @see m.component * @see m.component
*/ */
view(ctrl: T): MithrilVirtualElement<T>; view(ctrl?: T, opts?: any): MithrilVirtualElement<T>;
} }
/** /**
@ -702,8 +740,7 @@ interface MithrilErrorCallback<T> {
*/ */
interface Thennable<T> { interface Thennable<T> {
then<U>(success: (value: T) => U): Thennable<U>; then<U>(success: (value: T) => U): Thennable<U>;
then<U,V>(success: (value: T) => U, then<U,V>(success: (value: T) => U, error: (value: Error) => V): Thennable<U>|Thennable<V>;
error: (value: Error) => V): Thennable<U>|Thennable<V>;
catch?: <U>(error: (value: Error) => U) => Thennable<U>; catch?: <U>(error: (value: Error) => U) => Thennable<U>;
} }
@ -728,8 +765,10 @@ interface MithrilPromise<T> extends Thennable<T>, MithrilProperty<MithrilPromise
* @param error The callback to call when the promise is rejected. * @param error The callback to call when the promise is rejected.
* @return The chained promise. * @return The chained promise.
*/ */
then<U, V>(success: MithrilSuccessCallback<T, U>, then<U, V>(
error: MithrilErrorCallback<V>): MithrilPromise<U> | MithrilPromise<V>; success: MithrilSuccessCallback<T, U>,
error: MithrilErrorCallback<V>
): MithrilPromise<U> | MithrilPromise<V>;
/** /**
* Chain this promise with a single error callback, without propogating * Chain this promise with a single error callback, without propogating
@ -877,3 +916,11 @@ interface MithrilXHROptions<T> {
*/ */
callbackKey?: string; callbackKey?: string;
} }
}
declare var Mithril: _mithril.MithrilStatic;
declare var m: _mithril.MithrilStatic;
declare module "mithril" {
export = m;
}

View File

@ -30,6 +30,16 @@ System.config({
defaultJSExtensions: true, defaultJSExtensions: true,
}); });
// Register mithril as a module,
// but only if it is ambient.
if (m) {
let mod = System.newModule({default: m});
let modName = "mithril";
System.set(modName, mod);
}
let me = window.location.protocol let me = window.location.protocol
+ "//" + window.location.host + "//" + window.location.host
+ window.location.pathname.replace(/[.]html$/, ".js"); + window.location.pathname.replace(/[.]html$/, ".js");

File diff suppressed because it is too large Load Diff

View File

@ -144,6 +144,7 @@ System.register(["./wallet", "./db", "./http"], function(exports_1, context_1) {
return handlers[req.type](db, req.detail, onresponse); return handlers[req.type](db, req.detail, onresponse);
} }
console.error("Request type " + JSON.stringify(req) + " unknown, req " + req.type); console.error("Request type " + JSON.stringify(req) + " unknown, req " + req.type);
onresponse({ error: "request unknown" });
return false; return false;
}); });
}) })

View File

@ -29,6 +29,7 @@ import {BrowserHttpLib} from "./http";
* @author Florian Dold * @author Florian Dold
*/ */
function makeHandlers(wallet: Wallet) { function makeHandlers(wallet: Wallet) {
return { return {
["balances"]: function(db, detail, sendResponse) { ["balances"]: function(db, detail, sendResponse) {
@ -162,6 +163,7 @@ export function wxMain() {
return handlers[req.type](db, req.detail, onresponse); return handlers[req.type](db, req.detail, onresponse);
} }
console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`); console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
onresponse({error: "request unknown"});
return false; return false;
}); });
}) })

View File

@ -24,7 +24,7 @@
<section id="main"> <section id="main">
<article> <article>
<div id="mint-selection"></div> <div class="fade" id="mint-selection"></div>
</article> </article>
</section> </section>

View File

@ -13,46 +13,71 @@
You should have received a copy of the GNU General Public License along with 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/> TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/ */
System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(exports_1, context_1) { System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril"], function(exports_1, context_1) {
"use strict"; "use strict";
var __moduleName = context_1 && context_1.id; var __moduleName = context_1 && context_1.id;
var helpers_1, types_1; var helpers_1, types_1, mithril_1;
var DelayTimer, Controller; var DelayTimer, Controller;
function view(ctrl) {
var controls = [];
var mx = function (x) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return controls.push(mithril_1.default.apply(void 0, [x].concat(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(ctrl.amount))));
mx("input", {
className: "url",
type: "text",
spellcheck: false,
value: ctrl.url(),
oninput: mithril_1.default.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
});
mx("button", {
onclick: function () { return ctrl.confirmReserve(ctrl.url(), ctrl.amount, ctrl.callbackUrl); },
disabled: !ctrl.isValidMint
}, "Confirm mint selection");
if (ctrl.statusString) {
mx("p", ctrl.statusString);
}
else {
mx("p", "Checking URL, please wait ...");
}
return mithril_1.default("div", controls);
var _a;
}
function getSuggestedMint(currency) {
// TODO: make this request go to the wallet backend
// Right now, this is a stub.
var defaultMint = {
"KUDOS": "http://mint.test.taler.net"
};
var mint = defaultMint[currency];
if (!mint) {
mint = "";
}
return Promise.resolve(mint);
}
function main() { function main() {
var url = URI(document.location.href); var url = URI(document.location.href);
var query = URI.parseQuery(url.query()); var query = URI.parseQuery(url.query());
var amount = types_1.AmountJson.checked(JSON.parse(query.amount)); var amount = types_1.AmountJson.checked(JSON.parse(query.amount));
var callback_url = query.callback_url; var callback_url = query.callback_url;
var MintSelection = { var bank_url = query.bank_url;
controller: function () { return new Controller(); }, getSuggestedMint(amount.currency)
view: function (ctrl) { .then(function (suggestedMintUrl) {
var controls = []; var controller = function () { return new Controller(suggestedMintUrl, amount, callback_url); };
var mx = function () { var MintSelection = { controller: controller, view: view };
var args = []; mithril_1.default.mount(document.getElementById("mint-selection"), MintSelection);
for (var _i = 0; _i < arguments.length; _i++) { })
args[_i - 0] = arguments[_i]; .catch(function (e) {
} // TODO: provide more context information, maybe factor it out into a
return controls.push(m.apply(void 0, args)); // TODO:generic error reporting function or component.
}; document.body.innerText = "Fatal error: \"" + e.message + "\".";
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)))); console.error("got backend error \"" + e.message + "\"");
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");
}
if (ctrl.errorString) {
mx("p", ctrl.errorString);
}
return m("div", controls);
var _a;
}
};
m.mount(document.getElementById("mint-selection"), MintSelection);
} }
exports_1("main", main); exports_1("main", main);
return { return {
@ -62,6 +87,9 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(expor
}, },
function (types_1_1) { function (types_1_1) {
types_1 = types_1_1; types_1 = types_1_1;
},
function (mithril_1_1) {
mithril_1 = mithril_1_1;
}], }],
execute: function() { execute: function() {
"use strict"; "use strict";
@ -77,39 +105,48 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(expor
} }
DelayTimer.prototype.bump = function () { DelayTimer.prototype.bump = function () {
var _this = this; var _this = this;
if (this.timerId !== null) { this.stop();
window.clearTimeout(this.timerId);
}
var handler = function () { var handler = function () {
_this.f(); _this.f();
}; };
this.timerId = window.setTimeout(handler, this.ms); this.timerId = window.setTimeout(handler, this.ms);
}; };
DelayTimer.prototype.stop = function () {
if (this.timerId !== null) {
window.clearTimeout(this.timerId);
}
};
return DelayTimer; return DelayTimer;
}()); }());
Controller = (function () { Controller = (function () {
function Controller() { function Controller(initialMintUrl, amount, callbackUrl) {
var _this = this; var _this = this;
this.url = null; this.url = mithril_1.default.prop();
this.errorString = null; this.statusString = null;
this.isValidMint = false; this.isValidMint = false;
this.update(); this.amount = amount;
this.callbackUrl = callbackUrl;
this.timer = new DelayTimer(800, function () { return _this.update(); }); this.timer = new DelayTimer(800, function () { return _this.update(); });
this.url(initialMintUrl);
this.update();
} }
Controller.prototype.update = function () { Controller.prototype.update = function () {
var _this = this; var _this = this;
this.timer.stop();
var doUpdate = function () { var doUpdate = function () {
if (!_this.url) { if (!_this.url()) {
_this.errorString = (_a = ["Please enter a URL"], _a.raw = ["Please enter a URL"], i18n(_a)); _this.statusString = (_a = ["Please enter a URL"], _a.raw = ["Please enter a URL"], i18n(_a));
mithril_1.default.endComputation();
return; return;
} }
_this.errorString = null; _this.statusString = null;
var parsedUrl = URI(_this.url); var parsedUrl = URI(_this.url());
if (parsedUrl.is("relative")) { 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)); _this.statusString = (_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));
mithril_1.default.endComputation();
return; return;
} }
var keysUrl = URI("/keys").absoluteTo(helpers_1.canonicalizeBaseUrl(_this.url)); var keysUrl = URI("/keys").absoluteTo(helpers_1.canonicalizeBaseUrl(_this.url()));
console.log("requesting keys from '" + keysUrl + "'"); console.log("requesting keys from '" + keysUrl + "'");
_this.request = new XMLHttpRequest(); _this.request = new XMLHttpRequest();
_this.request.onreadystatechange = function () { _this.request.onreadystatechange = function () {
@ -117,33 +154,33 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(expor
switch (_this.request.status) { switch (_this.request.status) {
case 200: case 200:
_this.isValidMint = true; _this.isValidMint = true;
_this.statusString = "The mint base URL is valid!";
break; break;
case 0: case 0:
_this.errorString = "unknown request error"; _this.statusString = "unknown request error";
break; break;
default: default:
_this.errorString = "request failed with status " + _this.request.status; _this.statusString = "request failed with status " + _this.request.status;
break; break;
} }
m.redraw();
} }
mithril_1.default.endComputation();
}; };
_this.request.open("get", keysUrl.href()); _this.request.open("get", keysUrl.href());
_this.request.send(); _this.request.send();
var _a, _b; var _a, _b;
}; };
mithril_1.default.startComputation();
doUpdate(); doUpdate();
m.redraw();
console.log("got update"); console.log("got update");
}; };
Controller.prototype.reset = function () { Controller.prototype.reset = function () {
this.isValidMint = false; this.isValidMint = false;
this.errorString = null; this.statusString = null;
if (this.request) { if (this.request) {
this.request.abort(); this.request.abort();
this.request = null; this.request = null;
} }
m.redraw();
}; };
Controller.prototype.confirmReserve = function (mint, amount, callback_url) { Controller.prototype.confirmReserve = function (mint, amount, callback_url) {
var _this = this; var _this = this;
@ -170,7 +207,7 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(expor
} }
else { else {
_this.reset(); _this.reset();
_this.errorString = ("Oops, something went wrong." + _this.statusString = ("Oops, something went wrong." +
("The wallet responded with error status (" + rawResp.error + ").")); ("The wallet responded with error status (" + rawResp.error + ")."));
} }
}; };
@ -178,7 +215,7 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types"], function(expor
}; };
Controller.prototype.onUrlChanged = function (url) { Controller.prototype.onUrlChanged = function (url) {
this.reset(); this.reset();
this.url = url; this.url(url);
this.timer.bump(); this.timer.bump();
}; };
return Controller; return Controller;

View File

@ -14,14 +14,14 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> 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 {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers";
import {AmountJson, CreateReserveResponse} from "../lib/wallet/types"; import {AmountJson, CreateReserveResponse} from "../lib/wallet/types";
import m from "mithril";
"use strict"; "use strict";
declare var m: any;
/** /**
* Execute something after a delay, with the possibility * Execute something after a delay, with the possibility
* to reset the delay. * to reset the delay.
@ -37,43 +37,55 @@ class DelayTimer {
} }
bump() { bump() {
if (this.timerId !== null) { this.stop();
window.clearTimeout(this.timerId);
}
const handler = () => { const handler = () => {
this.f(); this.f();
}; };
this.timerId = window.setTimeout(handler, this.ms); this.timerId = window.setTimeout(handler, this.ms);
} }
stop() {
if (this.timerId !== null) {
window.clearTimeout(this.timerId);
}
}
} }
class Controller { class Controller {
url = null; url = m.prop<string>();
errorString = null; statusString = null;
isValidMint = false; isValidMint = false;
private timer: DelayTimer; private timer: DelayTimer;
private request: XMLHttpRequest; private request: XMLHttpRequest;
amount: AmountJson;
callbackUrl: string;
constructor() { constructor(initialMintUrl: string, amount: AmountJson, callbackUrl: string) {
this.update(); this.amount = amount;
this.callbackUrl = callbackUrl;
this.timer = new DelayTimer(800, () => this.update()); this.timer = new DelayTimer(800, () => this.update());
this.url(initialMintUrl);
this.update();
} }
update() { private update() {
this.timer.stop();
const doUpdate = () => { const doUpdate = () => {
if (!this.url) { if (!this.url()) {
this.errorString = i18n`Please enter a URL`; this.statusString = i18n`Please enter a URL`;
m.endComputation();
return; return;
} }
this.errorString = null; this.statusString = null;
let parsedUrl = URI(this.url); let parsedUrl = URI(this.url());
if (parsedUrl.is("relative")) { if (parsedUrl.is("relative")) {
this.errorString = i18n`The URL you've entered is not valid (must be absolute)`; this.statusString = i18n`The URL you've entered is not valid (must be absolute)`;
m.endComputation();
return; return;
} }
const keysUrl = URI("/keys").absoluteTo(canonicalizeBaseUrl(this.url)); const keysUrl = URI("/keys").absoluteTo(canonicalizeBaseUrl(this.url()));
console.log(`requesting keys from '${keysUrl}'`); console.log(`requesting keys from '${keysUrl}'`);
@ -83,34 +95,36 @@ class Controller {
switch (this.request.status) { switch (this.request.status) {
case 200: case 200:
this.isValidMint = true; this.isValidMint = true;
this.statusString = "The mint base URL is valid!";
break; break;
case 0: case 0:
this.errorString = `unknown request error`; this.statusString = `unknown request error`;
break; break;
default: default:
this.errorString = `request failed with status ${this.request.status}`; this.statusString = `request failed with status ${this.request.status}`;
break; break;
} }
m.redraw();
} }
m.endComputation();
}; };
this.request.open("get", keysUrl.href()); this.request.open("get", keysUrl.href());
this.request.send(); this.request.send();
}; };
m.startComputation();
doUpdate(); doUpdate();
m.redraw();
console.log("got update"); console.log("got update");
} }
reset() { reset() {
this.isValidMint = false; this.isValidMint = false;
this.errorString = null; this.statusString = null;
if (this.request) { if (this.request) {
this.request.abort(); this.request.abort();
this.request = null; this.request = null;
} }
m.redraw();
} }
confirmReserve(mint: string, amount: AmountJson, callback_url: string) { confirmReserve(mint: string, amount: AmountJson, callback_url: string) {
@ -136,7 +150,7 @@ class Controller {
document.location.href = url.href(); document.location.href = url.href();
} else { } else {
this.reset(); this.reset();
this.errorString = ( this.statusString = (
`Oops, something went wrong.` + `Oops, something went wrong.` +
`The wallet responded with error status (${rawResp.error}).`); `The wallet responded with error status (${rawResp.error}).`);
} }
@ -146,50 +160,80 @@ class Controller {
onUrlChanged(url: string) { onUrlChanged(url: string) {
this.reset(); this.reset();
this.url = url; this.url(url);
this.timer.bump(); this.timer.bump();
} }
} }
function view(ctrl: Controller) {
let controls = [];
let mx = (x: string, ...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 mint selection");
if (ctrl.statusString) {
mx("p", ctrl.statusString);
} else {
mx("p", "Checking URL, please wait ...");
}
return m("div", controls);
}
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://mint.test.taler.net"
};
let mint = defaultMint[currency];
if (!mint) {
mint = ""
}
return Promise.resolve(mint);
}
export function main() { export function main() {
const url = URI(document.location.href); const url = URI(document.location.href);
const query: any = URI.parseQuery(url.query()); const query: any = URI.parseQuery(url.query());
const amount = AmountJson.checked(JSON.parse(query.amount)); const amount = AmountJson.checked(JSON.parse(query.amount));
const callback_url = query.callback_url; const callback_url = query.callback_url;
const bank_url = query.bank_url;
var MintSelection = { getSuggestedMint(amount.currency)
controller: () => new Controller(), .then((suggestedMintUrl) => {
view(ctrl: Controller) { const controller = () => new Controller(suggestedMintUrl, amount, callback_url);
let controls = []; var MintSelection = {controller, view};
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); 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}"`);
});
} }

View File

@ -101,3 +101,19 @@ button {
font-size: 120%; font-size: 120%;
padding: 0.5em; padding: 0.5em;
} }
/* We use fading to hide slower DOM updates */
.fade {
-webkit-animation: fade 0.7s;
animation: fade 0.7s;
opacity: 1;
}
@-webkit-keyframes fade {
from {opacity: 0}
to {opacity: 1}
}
@keyframes fade {
from {opacity: 0}
to {opacity: 1}
}