make queries then-able

This commit is contained in:
Florian Dold 2016-10-20 01:37:00 +02:00
parent 9ee0823b7e
commit de65019ed6
6 changed files with 132 additions and 69 deletions

76
lib/decl/lib.es6.d.ts vendored
View File

@ -20288,44 +20288,44 @@ declare function addEventListener(type: "mouseenter", listener: (this: Window, e
declare function addEventListener(type: "mouseleave", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mousemove", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseout", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseover", listener: (this: Window, MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseup", listener: (this: Window, MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mousewheel", listener: (this: Window, WheelEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "offline", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "online", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "orientationchange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pagehide", listener: (this: Window, PageTransitionEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pageshow", listener: (this: Window, PageTransitionEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pause", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "play", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "playing", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointercancel", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerdown", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerenter", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerleave", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointermove", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerout", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerover", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerup", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "popstate", listener: (this: Window, PopStateEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "progress", listener: (this: Window, ProgressEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "ratechange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "readystatechange", listener: (this: Window, ProgressEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "reset", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "resize", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "scroll", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "seeked", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "seeking", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "select", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "stalled", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "storage", listener: (this: Window, StorageEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "submit", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "suspend", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "timeupdate", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "unload", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "volumechange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "waiting", listener: (this: Window, Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "wheel", listener: (this: Window, WheelEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseover", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseup", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mousewheel", listener: (this: Window, ev: WheelEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "offline", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "online", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "orientationchange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pagehide", listener: (this: Window, ev: PageTransitionEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pageshow", listener: (this: Window, ev: PageTransitionEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pause", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "play", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "playing", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointercancel", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerdown", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerenter", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerleave", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointermove", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerout", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerover", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "pointerup", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "popstate", listener: (this: Window, ev: PopStateEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "progress", listener: (this: Window, ev: ProgressEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "ratechange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "readystatechange", listener: (this: Window, ev: ProgressEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "reset", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "resize", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "scroll", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "seeked", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "seeking", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "select", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "stalled", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "storage", listener: (this: Window, ev: StorageEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "submit", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "suspend", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "timeupdate", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "unload", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "volumechange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "waiting", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
declare function addEventListener(type: "wheel", listener: (this: Window, ev: WheelEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
type AAGUID = string;
type AlgorithmIdentifier = string | Algorithm;

View File

@ -69,6 +69,8 @@ export interface QueryStream<T> {
map<S>(f: (x:T) => S): QueryStream<S>;
flatMap<S>(f: (x: T) => S[]): QueryStream<S>;
toArray(): Promise<T[]>;
then(onfulfill: any, onreject: any): any;
}
export let AbortTransaction = Symbol("abort_transaction");
@ -92,7 +94,7 @@ function openPromise<T>() {
}
abstract class QueryStreamBase<T> implements QueryStream<T> {
abstract class QueryStreamBase<T> implements QueryStream<T>, PromiseLike<void> {
abstract subscribe(f: (isDone: boolean,
value: any,
tx: IDBTransaction) => void): void;
@ -103,11 +105,15 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
this.root = root;
}
flatMap<S>(f: (x: T) => T[]): QueryStream<S> {
return new QueryStreamFlatMap(this, f);
then<R>(onfulfilled: (value: void) => R | PromiseLike<R>, onrejected: (reason: any) => R | PromiseLike<R>): PromiseLike<R> {
return this.root.then(onfulfilled, onrejected);
}
map<S>(f: (x: T) => S): QueryStream<T> {
flatMap<S>(f: (x: T) => S[]): QueryStream<S> {
return new QueryStreamFlatMap<T,S>(this, f);
}
map<S>(f: (x: T) => S): QueryStream<S> {
return new QueryStreamMap(this, f);
}
@ -193,11 +199,11 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
}
class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
class QueryStreamFlatMap<T,S> extends QueryStreamBase<S> {
s: QueryStreamBase<T>;
flatMapFn: (v: T) => T[];
flatMapFn: (v: T) => S[];
constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => T[]) {
constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => S[]) {
super(s.root);
this.s = s;
this.flatMapFn = flatMapFn;
@ -218,11 +224,11 @@ class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
}
class QueryStreamMap<T> extends QueryStreamBase<T> {
s: QueryStreamBase<T>;
mapFn: (v: T) => T[];
class QueryStreamMap<S,T> extends QueryStreamBase<T> {
s: QueryStreamBase<S>;
mapFn: (v: S) => T;
constructor(s: QueryStreamBase<T>, mapFn: (v: T) => T[]) {
constructor(s: QueryStreamBase<S>, mapFn: (v: S) => T) {
super(s.root);
this.s = s;
this.mapFn = mapFn;
@ -364,7 +370,7 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
}
export class QueryRoot {
export class QueryRoot implements PromiseLike<void> {
private work: ((t: IDBTransaction) => void)[] = [];
private db: IDBDatabase;
private stores = new Set();
@ -376,18 +382,26 @@ export class QueryRoot {
*/
private hasWrite: boolean;
private finishScheduled: boolean;
constructor(db: IDBDatabase) {
this.db = db;
}
then<R>(onfulfilled: (value: void) => R | PromiseLike<R>, onrejected: (reason: any) => R | PromiseLike<R>): PromiseLike<R> {
return this.finish().then(onfulfilled, onrejected);
}
iter<T>(store: Store<T>): QueryStream<T> {
this.stores.add(store.name);
this.scheduleFinish();
return new IterQueryStream(this, store.name, {});
}
iterIndex<S extends IDBValidKey,T>(index: Index<S,T>,
only?: S): QueryStream<T> {
this.stores.add(index.storeName);
this.scheduleFinish();
return new IterQueryStream(this, index.storeName, {
only,
indexName: index.indexName
@ -403,6 +417,7 @@ export class QueryRoot {
let doPut = (tx: IDBTransaction) => {
tx.objectStore(store.name).put(val);
};
this.scheduleFinish();
this.addWork(doPut, store.name, true);
return this;
}
@ -427,6 +442,7 @@ export class QueryRoot {
tx.objectStore(store.name).put(m);
}
};
this.scheduleFinish();
this.addWork(doPut, store.name, true);
return this;
}
@ -443,6 +459,7 @@ export class QueryRoot {
tx.objectStore(store.name).put(obj);
}
};
this.scheduleFinish();
this.addWork(doPutAll, store.name, true);
return this;
}
@ -456,6 +473,7 @@ export class QueryRoot {
const doAdd = (tx: IDBTransaction) => {
tx.objectStore(store.name).add(val);
};
this.scheduleFinish();
this.addWork(doAdd, store.name, true);
return this;
}
@ -509,6 +527,13 @@ export class QueryRoot {
.then(() => promise);
}
private scheduleFinish() {
if (!this.finishScheduled) {
Promise.resolve().then(() => this.finish());
this.finishScheduled = true;
}
}
/**
* Finish the query, and start the query in the first place if necessary.
*/
@ -543,6 +568,7 @@ export class QueryRoot {
const doDelete = (tx: IDBTransaction) => {
tx.objectStore(storeName).delete(key);
};
this.scheduleFinish();
this.addWork(doDelete, storeName, true);
return this;
}

View File

@ -64,10 +64,15 @@ export interface ReserveRecord {
* be higher than the requested_amount
*/
requested_amount: AmountJson,
/**
* Amount we've already withdrawn from the reserve.
* What's the current amount that sits
* in precoins?
*/
withdrawn_amount: AmountJson;
precoin_amount: AmountJson;
confirmed: boolean,
}

View File

@ -33,7 +33,7 @@ import {
import {HttpResponse, RequestException} from "./http";
import {QueryRoot, Store, Index, JoinResult, AbortTransaction} from "./query";
import {Checkable} from "./checkable";
import {canonicalizeBaseUrl} from "./helpers";
import {canonicalizeBaseUrl, amountToPretty} from "./helpers";
import {ReserveCreationInfo, Amounts} from "./types";
import {PreCoin} from "./types";
import {CryptoApi} from "./cryptoApi";
@ -403,10 +403,13 @@ export class Wallet {
}
}
updateExchanges(): void {
async updateExchanges(): Promise<void> {
console.log("updating exchanges");
let exchangesUrls = this.q().iter(Stores.exchanges).map((e) => e.baseUrl);
let exchangesUrls = await this.q()
.iter(Stores.exchanges)
.map((e) => e.baseUrl)
.toArray();
for (let url of exchangesUrls) {
this.updateExchangeFromUrl(url)
@ -766,18 +769,18 @@ export class Wallet {
const coin = await this.withdrawExecute(preCoin);
const mutateReserve = (r: ReserveRecord) => {
let currentAmount = r.current_amount;
if (!currentAmount) {
throw Error("can't withdraw from reserve when current amount is" +
" unknown");
}
let x = Amounts.sub(currentAmount,
console.log(`before committing coin: current ${amountToPretty(r.current_amount!)}, precoin: ${amountToPretty(
r.precoin_amount)})}`);
let x = Amounts.sub(r.precoin_amount,
preCoin.coinValue,
denom!.fee_withdraw);
if (x.saturated) {
console.error("database inconsistent");
throw AbortTransaction;
}
r.current_amount = x.amount;
r.precoin_amount = x.amount;
return r;
};
@ -791,7 +794,6 @@ export class Wallet {
};
await this.q()
.put(Stores.precoins, preCoin)
.mutate(Stores.reserves, preCoin.reservePub, mutateReserve)
.delete("precoins", coin.coinPub)
.add(Stores.coins, coin)
@ -828,7 +830,7 @@ export class Wallet {
current_amount: null,
requested_amount: req.amount,
confirmed: false,
withdrawn_amount: Amounts.getZero(req.amount.currency)
precoin_amount: Amounts.getZero(req.amount.currency),
};
const historyEntry = {
@ -940,17 +942,47 @@ export class Wallet {
/**
* Withdraw coins from a reserve until it is empty.
*/
private async depleteReserve(reserve: any,
private async depleteReserve(reserve: ReserveRecord,
exchange: IExchangeInfo): Promise<number> {
if (!reserve.current_amount) {
throw Error("can't withdraw when amount is unknown");
}
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount!,
denomsAvailable);
let ps = denomsForWithdraw.map(async(denom) => {
function mutateReserve(r: ReserveRecord): ReserveRecord {
let currentAmount = r.current_amount;
if (!currentAmount) {
throw Error("can't withdraw when amount is unknown");
}
r.precoin_amount = Amounts.add(r.precoin_amount,
denom.value,
denom.fee_withdraw).amount;
let result = Amounts.sub(currentAmount,
denom.value,
denom.fee_withdraw);
if (result.saturated) {
console.error("can't create precoin, saturated");
throw AbortTransaction;
}
r.current_amount = result.amount;
console.log(`after creating precoin: current ${amountToPretty(r.current_amount)}, precoin: ${amountToPretty(
r.precoin_amount)})}`);
return r;
}
let preCoin = await this.cryptoApi
.createPreCoin(denom, reserve);
await this.q()
.put(Stores.precoins, preCoin)
.mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
await this.processPreCoin(preCoin);
});
await Promise.all(ps);
return ps.length;
}
@ -1219,6 +1251,7 @@ export class Wallet {
if (!amount) {
amount = r.requested_amount;
}
amount = Amounts.add(amount, r.precoin_amount).amount;
if (Amounts.cmp(smallestWithdraw[r.exchange_base_url], amount) < 0) {
entry.pendingIncoming = Amounts.add(entry.pendingIncoming,
amount).amount;
@ -1333,7 +1366,7 @@ export class Wallet {
oldDenom.fee_refresh));
function mutateCoin(c: Coin): Coin {
let r = Amounts.sub(coin.currentAmount,
let r = Amounts.sub(c.currentAmount,
refreshSession.valueWithFee);
if (r.saturated) {
// Something else must have written the coin value

View File

@ -270,7 +270,7 @@ class WalletBalanceView extends preact.Component<any, any> {
return i18n`Error: could not retrieve balance information.`;
}
if (!wallet) {
return this.renderEmpty();
return <span></span>;
}
console.log(wallet);
let listing = Object.keys(wallet).map((key) => {

View File

@ -17,7 +17,6 @@
"test/tests/taler.ts",
"lib/refs.d.ts",
"lib/i18n.ts",
"lib/shopApi.ts",
"lib/taler-wallet-lib.ts",
"lib/wallet/checkable.ts",
"lib/wallet/chromeBadge.ts",