aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-01-11 02:56:32 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-01-11 02:56:32 +0100
commitffe6dee6aa50c864cc9a36e816eb95f2f23719b1 (patch)
treedddcd8134d351241e5af8e016fa59848beace6d1
parent4f934925e017082606f2786e0bbf0d48281928d9 (diff)
downloadwallet-core-ffe6dee6aa50c864cc9a36e816eb95f2f23719b1.tar.xz
refactor code to be clearer/prettier
-rw-r--r--extension/lib/commonHelpers.ts6
-rw-r--r--extension/lib/emscripten/emsc.d.ts16
-rw-r--r--extension/lib/polyfill-react.ts23
-rw-r--r--extension/lib/wallet/emscriptif.ts34
-rw-r--r--extension/lib/wallet/http.ts5
-rw-r--r--extension/lib/wallet/query.ts237
-rw-r--r--extension/lib/wallet/timerThread.ts10
-rw-r--r--extension/lib/wallet/wallet.ts6
-rw-r--r--extension/lib/wallet/wxmessaging.js1
-rw-r--r--extension/lib/wallet/wxmessaging.ts2
-rw-r--r--extension/tsconfig.json1
11 files changed, 214 insertions, 127 deletions
diff --git a/extension/lib/commonHelpers.ts b/extension/lib/commonHelpers.ts
index 5c32e47c1..4974778b9 100644
--- a/extension/lib/commonHelpers.ts
+++ b/extension/lib/commonHelpers.ts
@@ -14,12 +14,12 @@
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;
- 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;
return v.toFixed(2);
});
diff --git a/extension/lib/emscripten/emsc.d.ts b/extension/lib/emscripten/emsc.d.ts
index 659457ca7..d65bd6dcb 100644
--- a/extension/lib/emscripten/emsc.d.ts
+++ b/extension/lib/emscripten/emsc.d.ts
@@ -19,23 +19,31 @@ export interface EmscFunGen {
ret: string,
args: string[]): ((...x: (number|string)[]) => any);
(name: string,
- ret: 'number',
+ ret: "number",
args: string[]): ((...x: (number|string)[]) => number);
(name: string,
- ret: 'void',
+ ret: "void",
args: string[]): ((...x: (number|string)[]) => void);
(name: string,
- ret: 'string',
+ ret: "string",
args: string[]): ((...x: (number|string)[]) => string);
}
export declare namespace Module {
var cwrap: EmscFunGen;
+
function _free(ptr: number);
+
function _malloc(n: number): number;
+
function Pointer_stringify(p: number, len?: number): string;
+
function getValue(ptr: number, type: string, noSafe?: boolean): number;
+
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);
} \ No newline at end of file
diff --git a/extension/lib/polyfill-react.ts b/extension/lib/polyfill-react.ts
index 8238093ab..a7fa8c395 100644
--- a/extension/lib/polyfill-react.ts
+++ b/extension/lib/polyfill-react.ts
@@ -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";
let React = {
@@ -7,7 +28,7 @@ let React = {
e.setAttribute(k, props[k]);
}
for (let child of children) {
- if ("string" === typeof child || "number" == typeof child) {
+ if ("string" === typeof child || "number" === typeof child) {
child = document.createTextNode(child);
}
e.appendChild(child);
diff --git a/extension/lib/wallet/emscriptif.ts b/extension/lib/wallet/emscriptif.ts
index eb6a292a7..223fe7348 100644
--- a/extension/lib/wallet/emscriptif.ts
+++ b/extension/lib/wallet/emscriptif.ts
@@ -36,7 +36,8 @@ const GNUNET_SYSERR = -1;
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 = {
free: (ptr) => Module._free(ptr),
@@ -447,8 +448,8 @@ abstract class PackedArenaObject extends ArenaObject {
bytes.push("0".concat(b.toString(16)).slice(-2));
}
let lines = [];
- for (let i = 0; i < bytes.length; i+=8) {
- lines.push(bytes.slice(i, i+8).join(","));
+ for (let i = 0; i < bytes.length; i += 8) {
+ lines.push(bytes.slice(i, i + 8).join(","));
}
return lines.join("\n");
}
@@ -459,6 +460,7 @@ export class AmountNbo extends PackedArenaObject {
size() {
return 24;
}
+
toJson(): any {
let a = new DefaultArena();
let am = new Amount(null, a);
@@ -526,6 +528,7 @@ export class EddsaPublicKey extends PackedArenaObject {
size() {
return 32;
}
+
static fromCrock: (s: string) => EddsaPublicKey;
}
mixinStatic(EddsaPublicKey, fromCrock);
@@ -543,7 +546,8 @@ function makeFromCrock(decodeFn: (p: number, s: number) => number) {
return fromCrock;
}
-function makeToCrock(encodeFn: (po: number, ps: number) => number): () => string {
+function makeToCrock(encodeFn: (po: number,
+ ps: number) => number): () => string {
function toCrock() {
let ptr = emscAlloc.malloc(PTR_SIZE);
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();
return s;
}
+
return toCrock;
}
@@ -699,7 +704,7 @@ abstract class SignatureStruct {
}
- toJson() {
+ toJson() {
let res: any = {};
for (let f of this.fieldTypes()) {
let name = f[0];
@@ -857,6 +862,7 @@ function makeEncode(encodeFn) {
emsc.free(ptr);
return res;
}
+
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;
encode: (arena?: Arena) => ByteArray;
@@ -901,9 +907,9 @@ mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
export function rsaBlind(hashCode: HashCode,
- blindingKey: RsaBlindingKey,
- pkey: RsaPublicKey,
- arena?: Arena): ByteArray {
+ blindingKey: RsaBlindingKey,
+ pkey: RsaPublicKey,
+ arena?: Arena): ByteArray {
let ptr = emscAlloc.malloc(PTR_SIZE);
let s = emscAlloc.rsa_blind(hashCode.nativePtr,
blindingKey.nativePtr,
@@ -914,8 +920,8 @@ export function rsaBlind(hashCode: HashCode,
export function eddsaSign(purpose: EccSignaturePurpose,
- priv: EddsaPrivateKey,
- a?: Arena): EddsaSignature {
+ priv: EddsaPrivateKey,
+ a?: Arena): EddsaSignature {
let sig = new EddsaSignature(a);
sig.alloc();
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,
- bk: RsaBlindingKey,
- pk: RsaPublicKey,
- a?: Arena): RsaSignature {
+ bk: RsaBlindingKey,
+ pk: RsaPublicKey,
+ a?: Arena): RsaSignature {
let x = new RsaSignature(a);
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
bk.nativePtr,
diff --git a/extension/lib/wallet/http.ts b/extension/lib/wallet/http.ts
index d132857b7..3f7244e40 100644
--- a/extension/lib/wallet/http.ts
+++ b/extension/lib/wallet/http.ts
@@ -23,7 +23,6 @@
"use strict";
-
export interface HttpResponse {
status: number;
responseText: string;
@@ -32,8 +31,8 @@ export interface HttpResponse {
export class BrowserHttpLib {
req(method: string,
- url: string|uri.URI,
- options?: any): Promise<HttpResponse> {
+ url: string|uri.URI,
+ options?: any): Promise<HttpResponse> {
let urlString: string;
if (url instanceof URI) {
urlString = url.href();
diff --git a/extension/lib/wallet/query.ts b/extension/lib/wallet/query.ts
index c67ce0193..dda716d07 100644
--- a/extension/lib/wallet/query.ts
+++ b/extension/lib/wallet/query.ts
@@ -30,9 +30,39 @@ export function Query(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;
@@ -40,14 +70,14 @@ abstract class QueryStreamBase {
this.root = root;
}
- indexJoin(storeName: string, indexName: string, key: any): QueryStreamBase {
- // join on the source relation's key, which may be
- // a path or a transformer function
- this.root.stores.add(storeName);
+ indexJoin<S>(storeName: string,
+ indexName: string,
+ key: any): QueryStream<[T,S]> {
+ this.root.addWork(null, storeName, false);
return new QueryStreamIndexJoin(this, storeName, indexName, key);
}
- filter(f: (any) => boolean): QueryStreamBase {
+ filter(f: (any) => boolean): QueryStream<T> {
return new QueryStreamFilter(this, f);
}
@@ -65,37 +95,39 @@ abstract class QueryStreamBase {
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 {
- s: QueryStreamBase;
+class QueryStreamFilter<T> extends QueryStreamBase<T> {
+ s: QueryStreamBase<T>;
filterFn;
- constructor(s: QueryStreamBase, filterFn) {
+ constructor(s: QueryStreamBase<T>, filterFn) {
super(s.root);
this.s = s;
this.filterFn = filterFn;
}
subscribe(f) {
- this.s.subscribe((isDone, value) => {
+ this.s.subscribe((isDone, value, tx) => {
if (isDone) {
- f(true, undefined);
+ f(true, undefined, tx);
return;
}
if (this.filterFn(value)) {
- f(false, value)
+ f(false, value, tx)
}
});
}
}
-class QueryStreamIndexJoin extends QueryStreamBase {
- s: QueryStreamBase;
+class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
+ s: QueryStreamBase<T>;
storeName;
key;
indexName;
@@ -109,48 +141,45 @@ class QueryStreamIndexJoin extends QueryStreamBase {
}
subscribe(f) {
- this.s.subscribe((isDone, value) => {
+ this.s.subscribe((isDone, value, tx) => {
if (isDone) {
- f(true, undefined);
+ f(true, undefined, tx);
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)));
req.onsuccess = () => {
let cursor = req.result;
if (cursor) {
- f(false, [value, cursor.value]);
+ f(false, [value, cursor.value], tx);
cursor.continue();
} else {
- f(true, undefined);
+ f(true, undefined, tx);
}
}
});
}
-
}
-class IterQueryStream extends QueryStreamBase {
- private qr: QueryRoot;
+class IterQueryStream<T> extends QueryStreamBase<T> {
private storeName;
private options;
constructor(qr, storeName, options?) {
super(qr);
- this.qr = qr;
this.options = options;
this.storeName = storeName;
}
subscribe(f) {
- function doIt() {
+ let doIt = (tx) => {
let s;
if (this.options && this.options.indexName) {
- s = this.qr.tx.objectStore(this.storeName)
- .index(this.options.indexName);
+ s = tx.objectStore(this.storeName)
+ .index(this.options.indexName);
} else {
- s = this.qr.tx.objectStore(this.storeName);
+ s = tx.objectStore(this.storeName);
}
let kr = undefined;
if (this.options && ("only" in this.options)) {
@@ -160,124 +189,158 @@ class IterQueryStream extends QueryStreamBase {
req.onsuccess = (e) => {
let cursor: IDBCursorWithValue = req.result;
if (cursor) {
- f(false, cursor.value);
+ f(false, cursor.value, tx);
cursor.continue();
} else {
- f(true, undefined);
+ f(true, undefined, tx);
}
}
- }
+ };
- this.qr.work.push(doIt.bind(this));
+ this.root.addWork(doIt, null, false);
}
}
class QueryRoot {
- work = [];
- db: IDBDatabase;
- tx: IDBTransaction;
- stores = new Set();
- kickoffPromise;
+ private work = [];
+ private db: IDBDatabase;
+ private stores = new Set();
+ private kickoffPromise;
+
+ /**
+ * Some operations is a write operation,
+ * and we need to do a "readwrite" transaction/
+ */
+ private hasWrite;
constructor(db) {
this.db = db;
}
- iter(storeName): QueryStreamBase {
+ iter<T>(storeName): QueryStream<T> {
this.stores.add(storeName);
return new IterQueryStream(this, storeName);
}
- iterOnly(storeName, key): QueryStreamBase {
+ iterOnly<T>(storeName, key): QueryStream<T> {
this.stores.add(storeName);
return new IterQueryStream(this, storeName, {only: key});
}
- iterIndex(storeName, indexName, key) {
+
+ iterIndex<T>(storeName, indexName, key) {
this.stores.add(storeName);
return new IterQueryStream(this, storeName, {indexName: indexName});
}
- put(storeName, val): QueryRoot {
- this.stores.add(storeName);
- function doPut() {
- this.tx.objectStore(storeName).put(val);
- }
- this.work.push(doPut.bind(this));
+ /**
+ * 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 {
+ let doPut = (tx: IDBTransaction) => {
+ tx.objectStore(storeName).put(val);
+ };
+ this.addWork(doPut, storeName, true);
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 {
- this.stores.add(storeName);
- function doPutAll() {
- for (let obj of iterable) {
- this.tx.objectStore(storeName).put(obj);
+ const doPutAll = (tx: IDBTransaction) => {
+ for (const obj of iterable) {
+ tx.objectStore(storeName).put(obj);
}
- }
-
- this.work.push(doPutAll.bind(this));
+ };
+ this.addWork(doPutAll, storeName, true);
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 {
- this.stores.add(storeName);
- function doAdd() {
- this.tx.objectStore(storeName).add(val);
- }
-
- this.work.push(doAdd.bind(this));
+ const doAdd = (tx: IDBTransaction) => {
+ tx.objectStore(storeName).add(val);
+ };
+ this.addWork(doAdd, storeName, true);
return this;
}
+ /**
+ * Get one object from a store by its key.
+ */
get(storeName, key): Promise<any> {
- this.stores.add(storeName);
- 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);
+ const {resolve, promise} = openPromise();
+
+ const doGet = (tx) => {
+ const req = tx.objectStore(storeName).get(key);
req.onsuccess = (r) => {
- leakedResolve(req.result);
+ resolve(req.result);
};
- }
+ };
- this.work.push(doGet.bind(this));
- return Promise.resolve().then(() => {
- return this.finish().then(() => p);
- });
+ 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> {
if (this.kickoffPromise) {
return this.kickoffPromise;
}
this.kickoffPromise = new Promise((resolve, reject) => {
-
- this.tx = this.db.transaction(Array.from(this.stores), "readwrite");
- this.tx.oncomplete = () => {
+ const mode = this.hasWrite ? "readwrite" : "readonly";
+ const tx = this.db.transaction(Array.from(this.stores), mode);
+ tx.oncomplete = () => {
resolve();
};
for (let w of this.work) {
- w();
+ w(tx);
}
});
return this.kickoffPromise;
}
+ /**
+ * Delete an object by from the given object store.
+ */
delete(storeName: string, key): QueryRoot {
- this.stores.add(storeName);
- function doDelete() {
- this.tx.objectStore(storeName).delete(key);
- }
-
- this.work.push(doDelete.bind(this));
+ const doDelete = (tx) => {
+ tx.objectStore(storeName).delete(key);
+ };
+ this.addWork(doDelete, storeName, true);
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);
+ }
+ }
} \ No newline at end of file
diff --git a/extension/lib/wallet/timerThread.ts b/extension/lib/wallet/timerThread.ts
deleted file mode 100644
index 6635da009..000000000
--- a/extension/lib/wallet/timerThread.ts
+++ /dev/null
@@ -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);
-}; \ No newline at end of file
diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts
index 46bae70a7..8dbcca044 100644
--- a/extension/lib/wallet/wallet.ts
+++ b/extension/lib/wallet/wallet.ts
@@ -462,8 +462,10 @@ export class Wallet {
.then((mint) =>
this.updateReserve(reservePub, mint)
.then((reserve) => this.depleteReserve(reserve,
- mint))
- );
+ mint)))
+ .catch((e) => {
+ console.error("Failed to deplete reserve", e.stack);
+ });
return resp;
});
});
diff --git a/extension/lib/wallet/wxmessaging.js b/extension/lib/wallet/wxmessaging.js
index 7a6f501be..d4df23a08 100644
--- a/extension/lib/wallet/wxmessaging.js
+++ b/extension/lib/wallet/wxmessaging.js
@@ -137,7 +137,6 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
};
return ChromeBadge;
}());
- wxMain();
}
}
});
diff --git a/extension/lib/wallet/wxmessaging.ts b/extension/lib/wallet/wxmessaging.ts
index c69361c14..123746f4d 100644
--- a/extension/lib/wallet/wxmessaging.ts
+++ b/extension/lib/wallet/wxmessaging.ts
@@ -132,4 +132,4 @@ export function wxMain() {
return false;
});
});
-}
+} \ No newline at end of file
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index e76d322af..7f98b3904 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -19,7 +19,6 @@
"lib/wallet/types.ts",
"lib/commonHelpers.ts",
"lib/polyfill-react.ts",
- "lib/wallet/timerThread.ts",
"content_scripts/notify.ts",
"background/main.ts",
"popup/balance-overview.tsx",