refactor code to be clearer/prettier

This commit is contained in:
Florian Dold 2016-01-11 02:56:32 +01:00
parent 4f934925e0
commit ffe6dee6aa
11 changed files with 216 additions and 129 deletions

View File

@ -14,12 +14,12 @@
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/>
*/ */
Handlebars.registerHelper('prettyAmount', function (amount) { Handlebars.registerHelper("prettyAmount", function (amount) {
let v = amount.value + amount.fraction / 1e6; let v = amount.value + amount.fraction / 1e6;
return v.toFixed(2) + " " + amount.currency; return `${v.toFixed(2)} ${amount.currency}`;
}); });
Handlebars.registerHelper('prettyAmountNoCurrency', function (amount) { Handlebars.registerHelper("prettyAmountNoCurrency", function (amount) {
let v = amount.value + amount.fraction / 1e6; let v = amount.value + amount.fraction / 1e6;
return v.toFixed(2); return v.toFixed(2);
}); });

View File

@ -19,23 +19,31 @@ export interface EmscFunGen {
ret: string, ret: string,
args: string[]): ((...x: (number|string)[]) => any); args: string[]): ((...x: (number|string)[]) => any);
(name: string, (name: string,
ret: 'number', ret: "number",
args: string[]): ((...x: (number|string)[]) => number); args: string[]): ((...x: (number|string)[]) => number);
(name: string, (name: string,
ret: 'void', ret: "void",
args: string[]): ((...x: (number|string)[]) => void); args: string[]): ((...x: (number|string)[]) => void);
(name: string, (name: string,
ret: 'string', ret: "string",
args: string[]): ((...x: (number|string)[]) => string); args: string[]): ((...x: (number|string)[]) => string);
} }
export declare namespace Module { export declare namespace Module {
var cwrap: EmscFunGen; var cwrap: EmscFunGen;
function _free(ptr: number); function _free(ptr: number);
function _malloc(n: number): number; function _malloc(n: number): number;
function Pointer_stringify(p: number, len?: number): string; function Pointer_stringify(p: number, len?: number): string;
function getValue(ptr: number, type: string, noSafe?: boolean): number; function getValue(ptr: number, type: string, noSafe?: boolean): number;
function setValue(ptr: number, value: number, type: string, noSafe?: boolean); function setValue(ptr: number, value: number, type: string, noSafe?: boolean);
function writeStringToMemory(s: string, buffer: number, dontAddNull?: boolean);
function writeStringToMemory(s: string,
buffer: number,
dontAddNull?: boolean);
} }

View File

@ -1,3 +1,24 @@
/*
This file is part of TALER
(C) 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/>
*/
/**
* Implement the "React" namespace so that we can use TSX literals.
* Just returns plain DOM elements, no fancy virtual DOM.
*/
"use strict"; "use strict";
let React = { let React = {
@ -7,7 +28,7 @@ let React = {
e.setAttribute(k, props[k]); e.setAttribute(k, props[k]);
} }
for (let child of children) { for (let child of children) {
if ("string" === typeof child || "number" == typeof child) { if ("string" === typeof child || "number" === typeof child) {
child = document.createTextNode(child); child = document.createTextNode(child);
} }
e.appendChild(child); e.appendChild(child);

View File

@ -36,7 +36,8 @@ const GNUNET_SYSERR = -1;
let Module = EmscWrapper.Module; let Module = EmscWrapper.Module;
let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null, args); let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null,
args);
var emsc = { var emsc = {
free: (ptr) => Module._free(ptr), free: (ptr) => Module._free(ptr),
@ -447,8 +448,8 @@ abstract class PackedArenaObject extends ArenaObject {
bytes.push("0".concat(b.toString(16)).slice(-2)); bytes.push("0".concat(b.toString(16)).slice(-2));
} }
let lines = []; let lines = [];
for (let i = 0; i < bytes.length; i+=8) { for (let i = 0; i < bytes.length; i += 8) {
lines.push(bytes.slice(i, i+8).join(",")); lines.push(bytes.slice(i, i + 8).join(","));
} }
return lines.join("\n"); return lines.join("\n");
} }
@ -459,6 +460,7 @@ export class AmountNbo extends PackedArenaObject {
size() { size() {
return 24; return 24;
} }
toJson(): any { toJson(): any {
let a = new DefaultArena(); let a = new DefaultArena();
let am = new Amount(null, a); let am = new Amount(null, a);
@ -526,6 +528,7 @@ export class EddsaPublicKey extends PackedArenaObject {
size() { size() {
return 32; return 32;
} }
static fromCrock: (s: string) => EddsaPublicKey; static fromCrock: (s: string) => EddsaPublicKey;
} }
mixinStatic(EddsaPublicKey, fromCrock); mixinStatic(EddsaPublicKey, fromCrock);
@ -543,7 +546,8 @@ function makeFromCrock(decodeFn: (p: number, s: number) => number) {
return fromCrock; return fromCrock;
} }
function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string { function makeToCrock(encodeFn: (po: number,
ps: number) => number): () => string {
function toCrock() { function toCrock() {
let ptr = emscAlloc.malloc(PTR_SIZE); let ptr = emscAlloc.malloc(PTR_SIZE);
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr); let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
@ -553,6 +557,7 @@ function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string
res.destroy(); res.destroy();
return s; return s;
} }
return toCrock; return toCrock;
} }
@ -699,7 +704,7 @@ abstract class SignatureStruct {
} }
toJson() { toJson() {
let res: any = {}; let res: any = {};
for (let f of this.fieldTypes()) { for (let f of this.fieldTypes()) {
let name = f[0]; let name = f[0];
@ -857,6 +862,7 @@ function makeEncode(encodeFn) {
emsc.free(ptr); emsc.free(ptr);
return res; return res;
} }
return encode; return encode;
} }
@ -886,7 +892,7 @@ export class EddsaSignature extends PackedArenaObject {
} }
export class RsaSignature extends ArenaObject implements Encodeable{ export class RsaSignature extends ArenaObject implements Encodeable {
static fromCrock: (s: string, a?: Arena) => RsaSignature; static fromCrock: (s: string, a?: Arena) => RsaSignature;
encode: (arena?: Arena) => ByteArray; encode: (arena?: Arena) => ByteArray;
@ -901,9 +907,9 @@ mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
export function rsaBlind(hashCode: HashCode, export function rsaBlind(hashCode: HashCode,
blindingKey: RsaBlindingKey, blindingKey: RsaBlindingKey,
pkey: RsaPublicKey, pkey: RsaPublicKey,
arena?: Arena): ByteArray { arena?: Arena): ByteArray {
let ptr = emscAlloc.malloc(PTR_SIZE); let ptr = emscAlloc.malloc(PTR_SIZE);
let s = emscAlloc.rsa_blind(hashCode.nativePtr, let s = emscAlloc.rsa_blind(hashCode.nativePtr,
blindingKey.nativePtr, blindingKey.nativePtr,
@ -914,8 +920,8 @@ export function rsaBlind(hashCode: HashCode,
export function eddsaSign(purpose: EccSignaturePurpose, export function eddsaSign(purpose: EccSignaturePurpose,
priv: EddsaPrivateKey, priv: EddsaPrivateKey,
a?: Arena): EddsaSignature { a?: Arena): EddsaSignature {
let sig = new EddsaSignature(a); let sig = new EddsaSignature(a);
sig.alloc(); sig.alloc();
let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr); let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
@ -927,9 +933,9 @@ export function eddsaSign(purpose: EccSignaturePurpose,
export function rsaUnblind(sig: RsaSignature, export function rsaUnblind(sig: RsaSignature,
bk: RsaBlindingKey, bk: RsaBlindingKey,
pk: RsaPublicKey, pk: RsaPublicKey,
a?: Arena): RsaSignature { a?: Arena): RsaSignature {
let x = new RsaSignature(a); let x = new RsaSignature(a);
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr, x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
bk.nativePtr, bk.nativePtr,

View File

@ -23,7 +23,6 @@
"use strict"; "use strict";
export interface HttpResponse { export interface HttpResponse {
status: number; status: number;
responseText: string; responseText: string;
@ -32,8 +31,8 @@ export interface HttpResponse {
export class BrowserHttpLib { export class BrowserHttpLib {
req(method: string, req(method: string,
url: string|uri.URI, url: string|uri.URI,
options?: any): Promise<HttpResponse> { options?: any): Promise<HttpResponse> {
let urlString: string; let urlString: string;
if (url instanceof URI) { if (url instanceof URI) {
urlString = url.href(); urlString = url.href();

View File

@ -30,9 +30,39 @@ export function Query(db) {
return new QueryRoot(db); return new QueryRoot(db);
} }
/**
* Stream that can be filtered, reduced or joined
* with indices.
*/
export interface QueryStream<T> {
indexJoin<S>(storeName: string,
indexName: string,
keyFn: (obj: any) => any): QueryStream<[T,S]>;
filter(f: (any) => boolean): QueryStream<T>;
reduce<S>(f: (S, T) => S, acc?: S): Promise<S>;
}
abstract class QueryStreamBase {
abstract subscribe(f: (isDone: boolean, value: any) => void); /**
* Get an unresolved promise together with its extracted resolve / reject
* function.
*
* @returns {{resolve: any, reject: any, promise: Promise<T>}}
*/
function openPromise<T>() {
let resolve, reject;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return {resolve, reject, promise};
}
abstract class QueryStreamBase<T> implements QueryStream<T> {
abstract subscribe(f: (isDone: boolean,
value: any,
tx: IDBTransaction) => void);
root: QueryRoot; root: QueryRoot;
@ -40,14 +70,14 @@ abstract class QueryStreamBase {
this.root = root; this.root = root;
} }
indexJoin(storeName: string, indexName: string, key: any): QueryStreamBase { indexJoin<S>(storeName: string,
// join on the source relation's key, which may be indexName: string,
// a path or a transformer function key: any): QueryStream<[T,S]> {
this.root.stores.add(storeName); this.root.addWork(null, storeName, false);
return new QueryStreamIndexJoin(this, storeName, indexName, key); return new QueryStreamIndexJoin(this, storeName, indexName, key);
} }
filter(f: (any) => boolean): QueryStreamBase { filter(f: (any) => boolean): QueryStream<T> {
return new QueryStreamFilter(this, f); return new QueryStreamFilter(this, f);
} }
@ -65,37 +95,39 @@ abstract class QueryStreamBase {
acc = f(value, acc); acc = f(value, acc);
}); });
return Promise.resolve().then(() => this.root.finish().then(() => p)); return Promise.resolve()
.then(() => this.root.finish())
.then(() => p);
} }
} }
class QueryStreamFilter extends QueryStreamBase { class QueryStreamFilter<T> extends QueryStreamBase<T> {
s: QueryStreamBase; s: QueryStreamBase<T>;
filterFn; filterFn;
constructor(s: QueryStreamBase, filterFn) { constructor(s: QueryStreamBase<T>, filterFn) {
super(s.root); super(s.root);
this.s = s; this.s = s;
this.filterFn = filterFn; this.filterFn = filterFn;
} }
subscribe(f) { subscribe(f) {
this.s.subscribe((isDone, value) => { this.s.subscribe((isDone, value, tx) => {
if (isDone) { if (isDone) {
f(true, undefined); f(true, undefined, tx);
return; return;
} }
if (this.filterFn(value)) { if (this.filterFn(value)) {
f(false, value) f(false, value, tx)
} }
}); });
} }
} }
class QueryStreamIndexJoin extends QueryStreamBase { class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
s: QueryStreamBase; s: QueryStreamBase<T>;
storeName; storeName;
key; key;
indexName; indexName;
@ -109,48 +141,45 @@ class QueryStreamIndexJoin extends QueryStreamBase {
} }
subscribe(f) { subscribe(f) {
this.s.subscribe((isDone, value) => { this.s.subscribe((isDone, value, tx) => {
if (isDone) { if (isDone) {
f(true, undefined); f(true, undefined, tx);
return; return;
} }
let s = this.root.tx.objectStore(this.storeName).index(this.indexName); let s = tx.objectStore(this.storeName).index(this.indexName);
let req = s.openCursor(IDBKeyRange.only(this.key(value))); let req = s.openCursor(IDBKeyRange.only(this.key(value)));
req.onsuccess = () => { req.onsuccess = () => {
let cursor = req.result; let cursor = req.result;
if (cursor) { if (cursor) {
f(false, [value, cursor.value]); f(false, [value, cursor.value], tx);
cursor.continue(); cursor.continue();
} else { } else {
f(true, undefined); f(true, undefined, tx);
} }
} }
}); });
} }
} }
class IterQueryStream extends QueryStreamBase { class IterQueryStream<T> extends QueryStreamBase<T> {
private qr: QueryRoot;
private storeName; private storeName;
private options; private options;
constructor(qr, storeName, options?) { constructor(qr, storeName, options?) {
super(qr); super(qr);
this.qr = qr;
this.options = options; this.options = options;
this.storeName = storeName; this.storeName = storeName;
} }
subscribe(f) { subscribe(f) {
function doIt() { let doIt = (tx) => {
let s; let s;
if (this.options && this.options.indexName) { if (this.options && this.options.indexName) {
s = this.qr.tx.objectStore(this.storeName) s = tx.objectStore(this.storeName)
.index(this.options.indexName); .index(this.options.indexName);
} else { } else {
s = this.qr.tx.objectStore(this.storeName); s = tx.objectStore(this.storeName);
} }
let kr = undefined; let kr = undefined;
if (this.options && ("only" in this.options)) { if (this.options && ("only" in this.options)) {
@ -160,124 +189,158 @@ class IterQueryStream extends QueryStreamBase {
req.onsuccess = (e) => { req.onsuccess = (e) => {
let cursor: IDBCursorWithValue = req.result; let cursor: IDBCursorWithValue = req.result;
if (cursor) { if (cursor) {
f(false, cursor.value); f(false, cursor.value, tx);
cursor.continue(); cursor.continue();
} else { } else {
f(true, undefined); f(true, undefined, tx);
} }
} }
} };
this.qr.work.push(doIt.bind(this)); this.root.addWork(doIt, null, false);
} }
} }
class QueryRoot { class QueryRoot {
work = []; private work = [];
db: IDBDatabase; private db: IDBDatabase;
tx: IDBTransaction; private stores = new Set();
stores = new Set(); private kickoffPromise;
kickoffPromise;
/**
* Some operations is a write operation,
* and we need to do a "readwrite" transaction/
*/
private hasWrite;
constructor(db) { constructor(db) {
this.db = db; this.db = db;
} }
iter(storeName): QueryStreamBase { iter<T>(storeName): QueryStream<T> {
this.stores.add(storeName); this.stores.add(storeName);
return new IterQueryStream(this, storeName); return new IterQueryStream(this, storeName);
} }
iterOnly(storeName, key): QueryStreamBase { iterOnly<T>(storeName, key): QueryStream<T> {
this.stores.add(storeName); this.stores.add(storeName);
return new IterQueryStream(this, storeName, {only: key}); return new IterQueryStream(this, storeName, {only: key});
} }
iterIndex(storeName, indexName, key) {
iterIndex<T>(storeName, indexName, key) {
this.stores.add(storeName); this.stores.add(storeName);
return new IterQueryStream(this, storeName, {indexName: indexName}); return new IterQueryStream(this, storeName, {indexName: indexName});
} }
/**
* Put an object into the given object store.
* Overrides if an existing object with the same key exists
* in the store.
*/
put(storeName, val): QueryRoot { put(storeName, val): QueryRoot {
this.stores.add(storeName); let doPut = (tx: IDBTransaction) => {
function doPut() { tx.objectStore(storeName).put(val);
this.tx.objectStore(storeName).put(val); };
} this.addWork(doPut, storeName, true);
this.work.push(doPut.bind(this));
return this; return this;
} }
/**
* Add all object from an iterable to the given object store.
* Fails if the object's key is already present
* in the object store.
*/
putAll(storeName, iterable): QueryRoot { putAll(storeName, iterable): QueryRoot {
this.stores.add(storeName); const doPutAll = (tx: IDBTransaction) => {
function doPutAll() { for (const obj of iterable) {
for (let obj of iterable) { tx.objectStore(storeName).put(obj);
this.tx.objectStore(storeName).put(obj);
} }
} };
this.addWork(doPutAll, storeName, true);
this.work.push(doPutAll.bind(this));
return this; return this;
} }
/**
* Add an object to the given object store.
* Fails if the object's key is already present
* in the object store.
*/
add(storeName, val): QueryRoot { add(storeName, val): QueryRoot {
this.stores.add(storeName); const doAdd = (tx: IDBTransaction) => {
function doAdd() { tx.objectStore(storeName).add(val);
this.tx.objectStore(storeName).add(val); };
} this.addWork(doAdd, storeName, true);
this.work.push(doAdd.bind(this));
return this; return this;
} }
/**
* Get one object from a store by its key.
*/
get(storeName, key): Promise<any> { get(storeName, key): Promise<any> {
this.stores.add(storeName); const {resolve, promise} = openPromise();
let leakedResolve;
let p = new Promise((resolve, reject) => {
leakedResolve = resolve;
});
if (!leakedResolve) {
// According to ES6 spec (paragraph 25.4.3.1), this can't happen.
throw Error("assertion failed");
}
function doGet() {
let req = this.tx.objectStore(storeName).get(key);
req.onsuccess = (r) => {
leakedResolve(req.result);
};
}
this.work.push(doGet.bind(this)); const doGet = (tx) => {
return Promise.resolve().then(() => { const req = tx.objectStore(storeName).get(key);
return this.finish().then(() => p); req.onsuccess = (r) => {
}); resolve(req.result);
};
};
this.addWork(doGet, storeName, false);
return Promise.resolve()
.then(() => this.finish())
.then(() => promise);
} }
/**
* Finish the query, and start the query in the first place if necessary.
*/
finish(): Promise<void> { finish(): Promise<void> {
if (this.kickoffPromise) { if (this.kickoffPromise) {
return this.kickoffPromise; return this.kickoffPromise;
} }
this.kickoffPromise = new Promise((resolve, reject) => { this.kickoffPromise = new Promise((resolve, reject) => {
const mode = this.hasWrite ? "readwrite" : "readonly";
this.tx = this.db.transaction(Array.from(this.stores), "readwrite"); const tx = this.db.transaction(Array.from(this.stores), mode);
this.tx.oncomplete = () => { tx.oncomplete = () => {
resolve(); resolve();
}; };
for (let w of this.work) { for (let w of this.work) {
w(); w(tx);
} }
}); });
return this.kickoffPromise; return this.kickoffPromise;
} }
/**
* Delete an object by from the given object store.
*/
delete(storeName: string, key): QueryRoot { delete(storeName: string, key): QueryRoot {
this.stores.add(storeName); const doDelete = (tx) => {
function doDelete() { tx.objectStore(storeName).delete(key);
this.tx.objectStore(storeName).delete(key); };
} this.addWork(doDelete, storeName, true);
this.work.push(doDelete.bind(this));
return this; return this;
} }
/**
* Low-level function to add a task to the internal work queue.
*/
addWork(workFn: (IDBTransaction) => void,
storeName: string,
isWrite: boolean) {
if (storeName) {
this.stores.add(storeName);
}
if (isWrite) {
this.hasWrite = true;
}
if (workFn) {
this.work.push(workFn);
}
}
} }

View File

@ -1,10 +0,0 @@
/**
* This file should be used as a WebWorker.
* Background pages in the WebExtensions model do
* not allow to schedule callbacks that should be called
* after a timeout. We can emulate this with WebWorkers.
*/
onmessage = function(e) {
self.setInterval(() => postMessage(true, "timerThread"), e.data.interval);
};

View File

@ -462,8 +462,10 @@ export class Wallet {
.then((mint) => .then((mint) =>
this.updateReserve(reservePub, mint) this.updateReserve(reservePub, mint)
.then((reserve) => this.depleteReserve(reserve, .then((reserve) => this.depleteReserve(reserve,
mint)) mint)))
); .catch((e) => {
console.error("Failed to deplete reserve", e.stack);
});
return resp; return resp;
}); });
}); });

View File

@ -137,7 +137,6 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
}; };
return ChromeBadge; return ChromeBadge;
}()); }());
wxMain();
} }
} }
}); });

View File

@ -132,4 +132,4 @@ export function wxMain() {
return false; return false;
}); });
}); });
} }

View File

@ -19,7 +19,6 @@
"lib/wallet/types.ts", "lib/wallet/types.ts",
"lib/commonHelpers.ts", "lib/commonHelpers.ts",
"lib/polyfill-react.ts", "lib/polyfill-react.ts",
"lib/wallet/timerThread.ts",
"content_scripts/notify.ts", "content_scripts/notify.ts",
"background/main.ts", "background/main.ts",
"popup/balance-overview.tsx", "popup/balance-overview.tsx",