re-format with prettier v2, fix HTML

This commit is contained in:
Florian Dold 2020-03-30 16:09:32 +05:30
parent 15e18440db
commit aaf950e2ad
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
92 changed files with 2165 additions and 2722 deletions

View File

@ -17,7 +17,7 @@
}, },
"scripts": { "scripts": {
"build": "make tsc", "build": "make tsc",
"pretty": "prettier --config .prettierrc --write 'src/**.ts'" "pretty": "prettier --config .prettierrc --write src"
}, },
"files": [ "files": [
"AUTHORS", "AUTHORS",

View File

@ -151,7 +151,7 @@ class AndroidWalletMessageHandler {
}; };
const w = await getDefaultNodeWallet(this.walletArgs); const w = await getDefaultNodeWallet(this.walletArgs);
this.maybeWallet = w; this.maybeWallet = w;
w.runRetryLoop().catch(e => { w.runRetryLoop().catch((e) => {
console.error("Error during wallet retry loop", e); console.error("Error during wallet retry loop", e);
}); });
this.wp.resolve(w); this.wp.resolve(w);
@ -250,7 +250,7 @@ class AndroidWalletMessageHandler {
this.maybeWallet = undefined; this.maybeWallet = undefined;
const w = await getDefaultNodeWallet(this.walletArgs); const w = await getDefaultNodeWallet(this.walletArgs);
this.maybeWallet = w; this.maybeWallet = w;
w.runRetryLoop().catch(e => { w.runRetryLoop().catch((e) => {
console.error("Error during wallet retry loop", e); console.error("Error during wallet retry loop", e);
}); });
this.wp.resolve(w); this.wp.resolve(w);

View File

@ -5,14 +5,14 @@
// Implementation derived from TweetNaCl version 20140427. // Implementation derived from TweetNaCl version 20140427.
// See for details: http://tweetnacl.cr.yp.to/ // See for details: http://tweetnacl.cr.yp.to/
const gf = function(init: number[] = []) { const gf = function (init: number[] = []) {
const r = new Float64Array(16); const r = new Float64Array(16);
if (init) for (let i = 0; i < init.length; i++) r[i] = init[i]; if (init) for (let i = 0; i < init.length; i++) r[i] = init[i];
return r; return r;
}; };
// Pluggable, initialized in high-level API below. // Pluggable, initialized in high-level API below.
let randombytes = function(x: Uint8Array, n: number): void { let randombytes = function (x: Uint8Array, n: number): void {
throw new Error("no PRNG"); throw new Error("no PRNG");
}; };
@ -2373,13 +2373,11 @@ function crypto_hash(out: Uint8Array, m: Uint8Array, n: number) {
ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); ts64(x, n - 8, (b / 0x20000000) | 0, b << 3);
crypto_hashblocks_hl(hh, hl, x, n); crypto_hashblocks_hl(hh, hl, x, n);
for (let i = 0; i < 8; i++) for (let i = 0; i < 8; i++) ts64(out, 8 * i, hh[i], hl[i]);
ts64(out, 8 * i, hh[i], hl[i]);
return 0; return 0;
} }
/** /**
* Incremental version of crypto_hash. * Incremental version of crypto_hash.
*/ */
@ -2416,7 +2414,7 @@ export class HashState {
let i = 0; let i = 0;
while (i < data.length) { while (i < data.length) {
const r = 128 - this.p; const r = 128 - this.p;
if (r > (data.length - i)) { if (r > data.length - i) {
for (let j = 0; i + j < data.length; j++) { for (let j = 0; i + j < data.length; j++) {
this.next[this.p + j] = data[i + j]; this.next[this.p + j] = data[i + j];
} }
@ -2447,8 +2445,7 @@ export class HashState {
ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); ts64(x, n - 8, (b / 0x20000000) | 0, b << 3);
crypto_hashblocks_hl(this.hh, this.hl, x, n); crypto_hashblocks_hl(this.hh, this.hl, x, n);
for (let i = 0; i < 8; i++) for (let i = 0; i < 8; i++) ts64(out, 8 * i, this.hh[i], this.hl[i]);
ts64(out, 8 * i, this.hh[i], this.hl[i]);
return out; return out;
} }
} }
@ -3078,7 +3075,7 @@ export function sign_ed25519_pk_to_curve25519(
return x25519_pk; return x25519_pk;
} }
(function() { (function () {
// Initialize PRNG if environment provides CSPRNG. // Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw. // If not, methods calling randombytes will throw.
const crypto = const crypto =
@ -3086,7 +3083,7 @@ export function sign_ed25519_pk_to_curve25519(
if (crypto && crypto.getRandomValues) { if (crypto && crypto.getRandomValues) {
// Browsers. // Browsers.
var QUOTA = 65536; var QUOTA = 65536;
setPRNG(function(x: Uint8Array, n: number) { setPRNG(function (x: Uint8Array, n: number) {
var i, var i,
v = new Uint8Array(n); v = new Uint8Array(n);
for (i = 0; i < n; i += QUOTA) { for (i = 0; i < n; i += QUOTA) {
@ -3099,7 +3096,7 @@ export function sign_ed25519_pk_to_curve25519(
// Node.js. // Node.js.
const cr = require("crypto"); const cr = require("crypto");
if (cr && cr.randomBytes) { if (cr && cr.randomBytes) {
setPRNG(function(x: Uint8Array, n: number) { setPRNG(function (x: Uint8Array, n: number) {
var i, var i,
v = cr.randomBytes(n); v = cr.randomBytes(n);
for (i = 0; i < n; i++) x[i] = v[i]; for (i = 0; i < n; i++) x[i] = v[i];

View File

@ -339,10 +339,7 @@ export class HMAC {
constructor(key: Uint8Array) { constructor(key: Uint8Array) {
const pad = new Uint8Array(this.blockSize); const pad = new Uint8Array(this.blockSize);
if (key.length > this.blockSize) { if (key.length > this.blockSize) {
new HashSha256() new HashSha256().update(key).finish(pad).clean();
.update(key)
.finish(pad)
.clean();
} else { } else {
for (let i = 0; i < key.length; i++) { for (let i = 0; i < key.length; i++) {
pad[i] = key[i]; pad[i] = key[i];

View File

@ -47,7 +47,7 @@ function bytesToHex(bytes: Uint8Array): string {
return hex.join(""); return hex.join("");
} }
test("encoding", t => { test("encoding", (t) => {
const utf8decoder = new TextDecoder("utf-8"); const utf8decoder = new TextDecoder("utf-8");
const utf8encoder = new TextEncoder(); const utf8encoder = new TextEncoder();
const s = "Hello, World"; const s = "Hello, World";
@ -57,7 +57,7 @@ test("encoding", t => {
t.deepEqual(s, sOut); t.deepEqual(s, sOut);
}); });
test("taler-exchange-tvg hash code", t => { test("taler-exchange-tvg hash code", (t) => {
const input = "91JPRV3F5GG4EKJN41A62V35E8"; const input = "91JPRV3F5GG4EKJN41A62V35E8";
const output = const output =
"CW96WR74JS8T53EC8GKSGD49QKH4ZNFTZXDAWMMV5GJ1E4BM6B8GPN5NVHDJ8ZVXNCW7Q4WBYCV61HCA3PZC2YJD850DT29RHHN7ESR"; "CW96WR74JS8T53EC8GKSGD49QKH4ZNFTZXDAWMMV5GJ1E4BM6B8GPN5NVHDJ8ZVXNCW7Q4WBYCV61HCA3PZC2YJD850DT29RHHN7ESR";
@ -67,7 +67,7 @@ test("taler-exchange-tvg hash code", t => {
t.deepEqual(myOutput, output); t.deepEqual(myOutput, output);
}); });
test("taler-exchange-tvg ecdhe key", t => { test("taler-exchange-tvg ecdhe key", (t) => {
const priv1 = "X4T4N0M8PVQXQEBW2BA7049KFSM7J437NSDFC6GDNM3N5J9367A0"; const priv1 = "X4T4N0M8PVQXQEBW2BA7049KFSM7J437NSDFC6GDNM3N5J9367A0";
const pub1 = "M997P494MS6A95G1P0QYWW2VNPSHSX5Q6JBY5B9YMNYWP0B50X3G"; const pub1 = "M997P494MS6A95G1P0QYWW2VNPSHSX5Q6JBY5B9YMNYWP0B50X3G";
const priv2 = "14A0MMQ64DCV8HE0CS3WBC9DHFJAHXRGV7NEARFJPC5R5E1697E0"; const priv2 = "14A0MMQ64DCV8HE0CS3WBC9DHFJAHXRGV7NEARFJPC5R5E1697E0";
@ -83,7 +83,7 @@ test("taler-exchange-tvg ecdhe key", t => {
t.deepEqual(encodeCrock(mySkm), skm); t.deepEqual(encodeCrock(mySkm), skm);
}); });
test("taler-exchange-tvg eddsa key", t => { test("taler-exchange-tvg eddsa key", (t) => {
const priv = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40"; const priv = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40";
const pub = "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0"; const pub = "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0";
@ -91,7 +91,7 @@ test("taler-exchange-tvg eddsa key", t => {
t.deepEqual(encodeCrock(pair.publicKey), pub); t.deepEqual(encodeCrock(pair.publicKey), pub);
}); });
test("taler-exchange-tvg kdf", t => { test("taler-exchange-tvg kdf", (t) => {
const salt = "94KPT83PCNS7J83KC5P78Y8"; const salt = "94KPT83PCNS7J83KC5P78Y8";
const ikm = "94KPT83MD1JJ0WV5CDS6AX10D5Q70XBM41NPAY90DNGQ8SBJD5GPR"; const ikm = "94KPT83MD1JJ0WV5CDS6AX10D5Q70XBM41NPAY90DNGQ8SBJD5GPR";
const ctx = const ctx =
@ -110,7 +110,7 @@ test("taler-exchange-tvg kdf", t => {
t.deepEqual(encodeCrock(myOut), out); t.deepEqual(encodeCrock(myOut), out);
}); });
test("taler-exchange-tvg eddsa_ecdh", t => { test("taler-exchange-tvg eddsa_ecdh", (t) => {
const priv_ecdhe = "4AFZWMSGTVCHZPQ0R81NWXDCK4N58G7SDBBE5KXE080Y50370JJG"; const priv_ecdhe = "4AFZWMSGTVCHZPQ0R81NWXDCK4N58G7SDBBE5KXE080Y50370JJG";
const pub_ecdhe = "FXFN5GPAFTKVPWJDPVXQ87167S8T82T5ZV8CDYC0NH2AE14X0M30"; const pub_ecdhe = "FXFN5GPAFTKVPWJDPVXQ87167S8T82T5ZV8CDYC0NH2AE14X0M30";
const priv_eddsa = "1KG54M8T3X8BSFSZXCR3SQBSR7Y9P53NX61M864S7TEVMJ2XVPF0"; const priv_eddsa = "1KG54M8T3X8BSFSZXCR3SQBSR7Y9P53NX61M864S7TEVMJ2XVPF0";
@ -137,7 +137,7 @@ test("taler-exchange-tvg eddsa_ecdh", t => {
t.deepEqual(encodeCrock(myKm2), key_material); t.deepEqual(encodeCrock(myKm2), key_material);
}); });
test("taler-exchange-tvg blind signing", t => { test("taler-exchange-tvg blind signing", (t) => {
const messageHash = const messageHash =
"TT1R28D79EJEJ9PC35AQS35CCG85DSXSZ508MV2HS2FN4ME6AHESZX5WP485R8A75KG53FN6F1YNW95008663TKAPWB81420VG17BY8"; "TT1R28D79EJEJ9PC35AQS35CCG85DSXSZ508MV2HS2FN4ME6AHESZX5WP485R8A75KG53FN6F1YNW95008663TKAPWB81420VG17BY8";
const rsaPublicKey = const rsaPublicKey =
@ -150,17 +150,28 @@ test("taler-exchange-tvg blind signing", t => {
const sig = const sig =
"PFT6WQJGCM9DE6264DJS6RMG4XDMCDBJKZGSXAF3BEXWZ979Q13NETKK05S1YV91CX3Y034FSS86SSHZTTE8097RRESQP52EKFGTWJXKHZJEQJ49YHMBNQDHW4CFBJECNJSV2PMHWVGXV7HB84R6P0S3ES559HWQX01Q9MYDEGRNHKW87QR2BNSG951D5NQGAKEJ2SSJBE18S6WYAC24FAP8TT8ANECH5371J0DJY0YR0VWAFWVJDV8XQSFXWMJ80N3A80SPSHPYJY3WZZXW63WQ46WHYY56ZSNE5G1RZ5CR0XYV2ECKPM8R0FS58EV16WTRAM1ABBFVNAT3CAEFAZCWP3XHPVBQY5NZVTD5QS2Q8SKJQ2XB30E11CWDN9KTV5CBK4DN72EVG73F3W3BATAKHG"; "PFT6WQJGCM9DE6264DJS6RMG4XDMCDBJKZGSXAF3BEXWZ979Q13NETKK05S1YV91CX3Y034FSS86SSHZTTE8097RRESQP52EKFGTWJXKHZJEQJ49YHMBNQDHW4CFBJECNJSV2PMHWVGXV7HB84R6P0S3ES559HWQX01Q9MYDEGRNHKW87QR2BNSG951D5NQGAKEJ2SSJBE18S6WYAC24FAP8TT8ANECH5371J0DJY0YR0VWAFWVJDV8XQSFXWMJ80N3A80SPSHPYJY3WZZXW63WQ46WHYY56ZSNE5G1RZ5CR0XYV2ECKPM8R0FS58EV16WTRAM1ABBFVNAT3CAEFAZCWP3XHPVBQY5NZVTD5QS2Q8SKJQ2XB30E11CWDN9KTV5CBK4DN72EVG73F3W3BATAKHG";
const myBm = rsaBlind(decodeCrock(messageHash), decodeCrock(bks), decodeCrock(rsaPublicKey)); const myBm = rsaBlind(
decodeCrock(messageHash),
decodeCrock(bks),
decodeCrock(rsaPublicKey),
);
t.deepEqual(encodeCrock(myBm), bm); t.deepEqual(encodeCrock(myBm), bm);
const mySig = rsaUnblind(decodeCrock(bs), decodeCrock(rsaPublicKey), decodeCrock(bks)); const mySig = rsaUnblind(
decodeCrock(bs),
decodeCrock(rsaPublicKey),
decodeCrock(bks),
);
t.deepEqual(encodeCrock(mySig), sig); t.deepEqual(encodeCrock(mySig), sig);
const v = rsaVerify(decodeCrock(messageHash), decodeCrock(sig), decodeCrock(rsaPublicKey)); const v = rsaVerify(
decodeCrock(messageHash),
decodeCrock(sig),
decodeCrock(rsaPublicKey),
);
t.true(v); t.true(v);
}); });
test("incremental hashing #1", (t) => { test("incremental hashing #1", (t) => {
const n = 1024; const n = 1024;
const d = nacl.randomBytes(n); const d = nacl.randomBytes(n);

View File

@ -281,8 +281,9 @@ export class CryptoApi {
CryptoApi.enableTracing && CryptoApi.enableTracing &&
console.log( console.log(
`rpc ${currentWorkItem.operation} took ${timer.performanceNow() - `rpc ${currentWorkItem.operation} took ${
currentWorkItem.startTime}ms`, timer.performanceNow() - currentWorkItem.startTime
}ms`,
); );
currentWorkItem.resolve(msg.data.result); currentWorkItem.resolve(msg.data.result);
} }

View File

@ -313,9 +313,7 @@ export class CryptoImplementation {
stringToBytes(paytoUri + "\0"), stringToBytes(paytoUri + "\0"),
new Uint8Array(0), new Uint8Array(0),
); );
const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS) const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS).put(h).build();
.put(h)
.build();
return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)); return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
} }
@ -467,8 +465,8 @@ export class CryptoImplementation {
exchangeBaseUrl, exchangeBaseUrl,
hash: encodeCrock(sessionHash), hash: encodeCrock(sessionHash),
meltCoinPub: meltCoin.coinPub, meltCoinPub: meltCoin.coinPub,
newDenomHashes: newCoinDenoms.map(d => d.denomPubHash), newDenomHashes: newCoinDenoms.map((d) => d.denomPubHash),
newDenoms: newCoinDenoms.map(d => d.denomPub), newDenoms: newCoinDenoms.map((d) => d.denomPub),
norevealIndex: undefined, norevealIndex: undefined,
planchetsForGammas: planchetsForGammas, planchetsForGammas: planchetsForGammas,
transferPrivs, transferPrivs,

View File

@ -96,7 +96,7 @@ export function handleWorkerMessage(msg: any) {
} }
}; };
handleRequest().catch(e => { handleRequest().catch((e) => {
console.error("error in node worker", e); console.error("error in node worker", e);
}); });
} }

View File

@ -37,12 +37,10 @@ export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
} }
} }
/** /**
* Worker implementation that uses node subprocesses. * Worker implementation that uses node subprocesses.
*/ */
export class SynchronousCryptoWorker { export class SynchronousCryptoWorker {
/** /**
* Function to be called when we receive a message from the worker thread. * Function to be called when we receive a message from the worker thread.
*/ */
@ -121,7 +119,7 @@ export class SynchronousCryptoWorker {
return; return;
} }
this.handleRequest(operation, id, args).catch(e => { this.handleRequest(operation, id, args).catch((e) => {
console.error("Error while handling crypto request:", e); console.error("Error while handling crypto request:", e);
}); });
} }

View File

@ -16,7 +16,12 @@
SPDX-License-Identifier: AGPL3.0-or-later SPDX-License-Identifier: AGPL3.0-or-later
*/ */
import { Headers, HttpRequestLibrary, HttpRequestOptions, HttpResponse } from "../util/http"; import {
Headers,
HttpRequestLibrary,
HttpRequestOptions,
HttpResponse,
} from "../util/http";
import { RequestThrottler } from "../util/RequestThrottler"; import { RequestThrottler } from "../util/RequestThrottler";
import Axios, { AxiosResponse } from "axios"; import Axios, { AxiosResponse } from "axios";
@ -85,10 +90,7 @@ export class NodeHttpLib implements HttpRequestLibrary {
}; };
} }
async get( async get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
url: string,
opt?: HttpRequestOptions,
): Promise<HttpResponse> {
return this.req("get", url, undefined, opt); return this.req("get", url, undefined, opt);
} }

View File

@ -340,7 +340,9 @@ export class CommandGroup<GN extends keyof any, TG> {
const r = splitOpt(opt); const r = splitOpt(opt);
const d = this.longOptions[r.key]; const d = this.longOptions[r.key];
if (!d) { if (!d) {
console.error(`error: unknown option '--${r.key}' for ${currentName}`); console.error(
`error: unknown option '--${r.key}' for ${currentName}`,
);
process.exit(-1); process.exit(-1);
throw Error("not reached"); throw Error("not reached");
} }
@ -359,7 +361,7 @@ export class CommandGroup<GN extends keyof any, TG> {
process.exit(-1); process.exit(-1);
throw Error("not reached"); throw Error("not reached");
} }
myArgs[d.name] = unparsedArgs[i+1]; myArgs[d.name] = unparsedArgs[i + 1];
i++; i++;
} else { } else {
myArgs[d.name] = r.value; myArgs[d.name] = r.value;
@ -427,14 +429,15 @@ export class CommandGroup<GN extends keyof any, TG> {
throw Error("not reached"); throw Error("not reached");
} }
for (let i = posArgIndex; i < this.arguments.length; i++) { for (let i = posArgIndex; i < this.arguments.length; i++) {
const d = this.arguments[i]; const d = this.arguments[i];
if (d.required) { if (d.required) {
if (d.args.default !== undefined) { if (d.args.default !== undefined) {
myArgs[d.name] = d.args.default; myArgs[d.name] = d.args.default;
} else { } else {
console.error(`error: missing positional argument '${d.name}' for ${currentName}`); console.error(
`error: missing positional argument '${d.name}' for ${currentName}`,
);
process.exit(-1); process.exit(-1);
throw Error("not reached"); throw Error("not reached");
} }
@ -447,7 +450,7 @@ export class CommandGroup<GN extends keyof any, TG> {
if (option.args.default !== undefined) { if (option.args.default !== undefined) {
myArgs[option.name] = option.args.default; myArgs[option.name] = option.args.default;
} else { } else {
const name = option.flagspec.join(",") const name = option.flagspec.join(",");
console.error(`error: missing option '${name}'`); console.error(`error: missing option '${name}'`);
process.exit(-1); process.exit(-1);
throw Error("not reached"); throw Error("not reached");
@ -584,10 +587,11 @@ export class Program<PN extends keyof any, T> {
} }
} }
export type GetArgType<T> = export type GetArgType<T> = T extends Program<any, infer AT>
T extends Program<any, infer AT> ? AT : ? AT
T extends CommandGroup<any, infer AT> ? AT : : T extends CommandGroup<any, infer AT>
any; ? AT
: any;
export function program<PN extends keyof any>( export function program<PN extends keyof any>(
argKey: PN, argKey: PN,
@ -596,15 +600,13 @@ export function program<PN extends keyof any>(
return new Program(argKey as string, args); return new Program(argKey as string, args);
} }
export function prompt(question: string): Promise<string> { export function prompt(question: string): Promise<string> {
const stdinReadline = readline.createInterface({ const stdinReadline = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout, output: process.stdout,
}); });
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
stdinReadline.question(question, res => { stdinReadline.question(question, (res) => {
resolve(res); resolve(res);
stdinReadline.close(); stdinReadline.close();
}); });

View File

@ -120,7 +120,9 @@ export async function getDefaultNodeWallet(
require("worker_threads"); require("worker_threads");
workerFactory = new NodeThreadCryptoWorkerFactory(); workerFactory = new NodeThreadCryptoWorkerFactory();
} catch (e) { } catch (e) {
console.log("worker threads not available, falling back to synchronous workers"); console.log(
"worker threads not available, falling back to synchronous workers",
);
workerFactory = new SynchronousCryptoWorkerFactory(); workerFactory = new SynchronousCryptoWorkerFactory();
} }
@ -161,7 +163,7 @@ export async function withdrawTestBalance(
myWallet.runRetryLoop().catch((x) => { myWallet.runRetryLoop().catch((x) => {
reject(x); reject(x);
}); });
myWallet.addNotificationListener(n => { myWallet.addNotificationListener((n) => {
if ( if (
n.type === NotificationType.ReserveDepleted && n.type === NotificationType.ReserveDepleted &&
n.reservePub === reservePub n.reservePub === reservePub

View File

@ -99,7 +99,7 @@ export async function runIntegrationTest(args: IntegrationTestArgs) {
const myWallet = await getDefaultNodeWallet({ httpLib: myHttpLib }); const myWallet = await getDefaultNodeWallet({ httpLib: myHttpLib });
myWallet.runRetryLoop().catch(e => { myWallet.runRetryLoop().catch((e) => {
console.error("exception during retry loop:", e); console.error("exception during retry loop:", e);
}); });
@ -233,7 +233,7 @@ export async function runIntegrationTestBasic(cfg: Configuration) {
persistentStoragePath: walletDbPath, persistentStoragePath: walletDbPath,
}); });
myWallet.runRetryLoop().catch(e => { myWallet.runRetryLoop().catch((e) => {
console.error("exception during retry loop:", e); console.error("exception during retry loop:", e);
}); });
@ -255,7 +255,12 @@ export async function runIntegrationTestBasic(cfg: Configuration) {
merchantApiKey, merchantApiKey,
); );
await makePayment(myWallet, myMerchant, Amounts.toString(parsedSpendAmount), "hello world"); await makePayment(
myWallet,
myMerchant,
Amounts.toString(parsedSpendAmount),
"hello world",
);
// Wait until the refresh is done // Wait until the refresh is done
await myWallet.runUntilDone(); await myWallet.runUntilDone();

View File

@ -23,7 +23,10 @@
* Imports. * Imports.
*/ */
import axios from "axios"; import axios from "axios";
import { CheckPaymentResponse, codecForCheckPaymentResponse } from "../types/talerTypes"; import {
CheckPaymentResponse,
codecForCheckPaymentResponse,
} from "../types/talerTypes";
/** /**
* Connection to the *internal* merchant backend. * Connection to the *internal* merchant backend.

File diff suppressed because it is too large Load Diff

View File

@ -34,8 +34,7 @@ export async function getBalancesInsideTransaction(
ws: InternalWalletState, ws: InternalWalletState,
tx: TransactionHandle, tx: TransactionHandle,
): Promise<WalletBalance> { ): Promise<WalletBalance> {
/**
/**
* Add amount to a balance field, both for * Add amount to a balance field, both for
* the slicing by exchange and currency. * the slicing by exchange and currency.
*/ */
@ -74,7 +73,7 @@ export async function getBalancesInsideTransaction(
byExchange: {}, byExchange: {},
}; };
await tx.iter(Stores.coins).forEach(c => { await tx.iter(Stores.coins).forEach((c) => {
if (c.suspended) { if (c.suspended) {
return; return;
} }
@ -82,7 +81,7 @@ export async function getBalancesInsideTransaction(
addTo(balanceStore, "available", c.currentAmount, c.exchangeBaseUrl); addTo(balanceStore, "available", c.currentAmount, c.exchangeBaseUrl);
} }
}); });
await tx.iter(Stores.refreshGroups).forEach(r => { await tx.iter(Stores.refreshGroups).forEach((r) => {
// Don't count finished refreshes, since the refresh already resulted // Don't count finished refreshes, since the refresh already resulted
// in coins being added to the wallet. // in coins being added to the wallet.
if (r.timestampFinished) { if (r.timestampFinished) {
@ -107,7 +106,7 @@ export async function getBalancesInsideTransaction(
} }
}); });
await tx.iter(Stores.withdrawalSession).forEach(wds => { await tx.iter(Stores.withdrawalSession).forEach((wds) => {
let w = wds.totalCoinValue; let w = wds.totalCoinValue;
for (let i = 0; i < wds.planchets.length; i++) { for (let i = 0; i < wds.planchets.length; i++) {
if (wds.withdrawn[i]) { if (wds.withdrawn[i]) {
@ -120,7 +119,7 @@ export async function getBalancesInsideTransaction(
addTo(balanceStore, "pendingIncoming", w, wds.exchangeBaseUrl); addTo(balanceStore, "pendingIncoming", w, wds.exchangeBaseUrl);
}); });
await tx.iter(Stores.purchases).forEach(t => { await tx.iter(Stores.purchases).forEach((t) => {
if (t.timestampFirstSuccessfulPay) { if (t.timestampFirstSuccessfulPay) {
return; return;
} }
@ -145,14 +144,16 @@ export async function getBalances(
): Promise<WalletBalance> { ): Promise<WalletBalance> {
logger.trace("starting to compute balance"); logger.trace("starting to compute balance");
return await ws.db.runWithReadTransaction([ return await ws.db.runWithReadTransaction(
Stores.coins, [
Stores.refreshGroups, Stores.coins,
Stores.reserves, Stores.refreshGroups,
Stores.purchases, Stores.reserves,
Stores.withdrawalSession, Stores.purchases,
], Stores.withdrawalSession,
async tx => { ],
return getBalancesInsideTransaction(ws, tx); async (tx) => {
}); return getBalancesInsideTransaction(ws, tx);
},
);
} }

View File

@ -64,7 +64,6 @@ export async function scrutinizeTalerJsonResponse<T>(
resp: HttpResponse, resp: HttpResponse,
codec: Codec<T>, codec: Codec<T>,
): Promise<T> { ): Promise<T> {
// FIXME: We should distinguish between different types of error status // FIXME: We should distinguish between different types of error status
// to react differently (throttle, report permanent failure) // to react differently (throttle, report permanent failure)
@ -82,7 +81,7 @@ export async function scrutinizeTalerJsonResponse<T>(
details: { details: {
httpStatusCode: resp.status, httpStatusCode: resp.status,
errorResponse: errorJson, errorResponse: errorJson,
} },
}); });
} catch (e) { } catch (e) {
const m = "could not parse response JSON"; const m = "could not parse response JSON";
@ -91,7 +90,7 @@ export async function scrutinizeTalerJsonResponse<T>(
message: m, message: m,
details: { details: {
status: resp.status, status: resp.status,
} },
}); });
} }
throw exc; throw exc;
@ -101,13 +100,13 @@ export async function scrutinizeTalerJsonResponse<T>(
json = await resp.json(); json = await resp.json();
} catch (e) { } catch (e) {
const m = "could not parse response JSON"; const m = "could not parse response JSON";
throw new OperationFailedError({ throw new OperationFailedError({
type: "network", type: "network",
message: m, message: m,
details: { details: {
status: resp.status, status: resp.status,
} },
}); });
} }
return codec.decode(json); return codec.decode(json);
} }
@ -138,7 +137,7 @@ export async function guardOperationException<T>(
type: "exception", type: "exception",
message: e.message, message: e.message,
details: {}, details: {},
} };
await onOpError(opErr); await onOpError(opErr);
throw new OperationFailedAndReportedError(opErr); throw new OperationFailedAndReportedError(opErr);
} }

View File

@ -193,7 +193,7 @@ async function updateExchangeWithKeys(
.currency; .currency;
const newDenominations = await Promise.all( const newDenominations = await Promise.all(
exchangeKeysJson.denoms.map(d => exchangeKeysJson.denoms.map((d) =>
denominationRecordFromKeys(ws, baseUrl, d), denominationRecordFromKeys(ws, baseUrl, d),
), ),
); );
@ -202,7 +202,7 @@ async function updateExchangeWithKeys(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.exchanges, Stores.denominations, Stores.recoupGroups, Stores.coins], [Stores.exchanges, Stores.denominations, Stores.recoupGroups, Stores.coins],
async tx => { async (tx) => {
const r = await tx.get(Stores.exchanges, baseUrl); const r = await tx.get(Stores.exchanges, baseUrl);
if (!r) { if (!r) {
console.warn(`exchange ${baseUrl} no longer present`); console.warn(`exchange ${baseUrl} no longer present`);
@ -275,7 +275,7 @@ async function updateExchangeWithKeys(
if (recoupGroupId) { if (recoupGroupId) {
// Asynchronously start recoup. This doesn't need to finish // Asynchronously start recoup. This doesn't need to finish
// for the exchange update to be considered finished. // for the exchange update to be considered finished.
processRecoupGroup(ws, recoupGroupId).catch(e => { processRecoupGroup(ws, recoupGroupId).catch((e) => {
console.log("error while recouping coins:", e); console.log("error while recouping coins:", e);
}); });
} }
@ -294,7 +294,7 @@ async function updateExchangeFinalize(
} }
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.exchanges, Stores.exchangeUpdatedEvents], [Stores.exchanges, Stores.exchangeUpdatedEvents],
async tx => { async (tx) => {
const r = await tx.get(Stores.exchanges, exchangeBaseUrl); const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
if (!r) { if (!r) {
return; return;
@ -338,7 +338,7 @@ async function updateExchangeWithTermsOfService(
const tosText = await resp.text(); const tosText = await resp.text();
const tosEtag = resp.headers.get("etag") || undefined; const tosEtag = resp.headers.get("etag") || undefined;
await ws.db.runWithWriteTransaction([Stores.exchanges], async tx => { await ws.db.runWithWriteTransaction([Stores.exchanges], async (tx) => {
const r = await tx.get(Stores.exchanges, exchangeBaseUrl); const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
if (!r) { if (!r) {
return; return;
@ -358,7 +358,7 @@ export async function acceptExchangeTermsOfService(
exchangeBaseUrl: string, exchangeBaseUrl: string,
etag: string | undefined, etag: string | undefined,
) { ) {
await ws.db.runWithWriteTransaction([Stores.exchanges], async tx => { await ws.db.runWithWriteTransaction([Stores.exchanges], async (tx) => {
const r = await tx.get(Stores.exchanges, exchangeBaseUrl); const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
if (!r) { if (!r) {
return; return;
@ -438,7 +438,7 @@ async function updateExchangeWithWireInfo(
feesForType[wireMethod] = feeList; feesForType[wireMethod] = feeList;
} }
await ws.db.runWithWriteTransaction([Stores.exchanges], async tx => { await ws.db.runWithWriteTransaction([Stores.exchanges], async (tx) => {
const r = await tx.get(Stores.exchanges, exchangeBaseUrl); const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
if (!r) { if (!r) {
return; return;
@ -500,7 +500,7 @@ async function updateExchangeFromUrlImpl(
}; };
await ws.db.put(Stores.exchanges, newExchangeRecord); await ws.db.put(Stores.exchanges, newExchangeRecord);
} else { } else {
await ws.db.runWithWriteTransaction([Stores.exchanges], async t => { await ws.db.runWithWriteTransaction([Stores.exchanges], async (t) => {
const rec = await t.get(Stores.exchanges, baseUrl); const rec = await t.get(Stores.exchanges, baseUrl);
if (!rec) { if (!rec) {
return; return;

View File

@ -47,7 +47,7 @@ import { timestampCmp } from "../util/time";
* Create an event ID from the type and the primary key for the event. * Create an event ID from the type and the primary key for the event.
*/ */
function makeEventId(type: HistoryEventType, ...args: string[]) { function makeEventId(type: HistoryEventType, ...args: string[]) {
return type + ";" + args.map(x => encodeURIComponent(x)).join(";"); return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
} }
function getOrderShortInfo( function getOrderShortInfo(
@ -72,7 +72,7 @@ async function collectProposalHistory(
history: HistoryEvent[], history: HistoryEvent[],
historyQuery?: HistoryQuery, historyQuery?: HistoryQuery,
) { ) {
tx.iter(Stores.proposals).forEachAsync(async proposal => { tx.iter(Stores.proposals).forEachAsync(async (proposal) => {
const status = proposal.proposalStatus; const status = proposal.proposalStatus;
switch (status) { switch (status) {
case ProposalStatus.ACCEPTED: case ProposalStatus.ACCEPTED:
@ -182,8 +182,8 @@ export async function getHistory(
Stores.reserveUpdatedEvents, Stores.reserveUpdatedEvents,
Stores.recoupGroups, Stores.recoupGroups,
], ],
async tx => { async (tx) => {
tx.iter(Stores.exchanges).forEach(exchange => { tx.iter(Stores.exchanges).forEach((exchange) => {
history.push({ history.push({
type: HistoryEventType.ExchangeAdded, type: HistoryEventType.ExchangeAdded,
builtIn: false, builtIn: false,
@ -196,7 +196,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.exchangeUpdatedEvents).forEach(eu => { tx.iter(Stores.exchangeUpdatedEvents).forEach((eu) => {
history.push({ history.push({
type: HistoryEventType.ExchangeUpdated, type: HistoryEventType.ExchangeUpdated,
eventId: makeEventId( eventId: makeEventId(
@ -208,7 +208,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.withdrawalSession).forEach(wsr => { tx.iter(Stores.withdrawalSession).forEach((wsr) => {
if (wsr.timestampFinish) { if (wsr.timestampFinish) {
const cs: PlanchetRecord[] = []; const cs: PlanchetRecord[] = [];
wsr.planchets.forEach((x) => { wsr.planchets.forEach((x) => {
@ -246,7 +246,7 @@ export async function getHistory(
await collectProposalHistory(tx, history, historyQuery); await collectProposalHistory(tx, history, historyQuery);
await tx.iter(Stores.payEvents).forEachAsync(async pe => { await tx.iter(Stores.payEvents).forEachAsync(async (pe) => {
const proposal = await tx.get(Stores.proposals, pe.proposalId); const proposal = await tx.get(Stores.proposals, pe.proposalId);
if (!proposal) { if (!proposal) {
return; return;
@ -262,7 +262,7 @@ export async function getHistory(
let verboseDetails: VerbosePayCoinDetails | undefined = undefined; let verboseDetails: VerbosePayCoinDetails | undefined = undefined;
if (historyQuery?.extraDebug) { if (historyQuery?.extraDebug) {
const coins: { const coins: {
value: string, value: string;
contribution: string; contribution: string;
denomPub: string; denomPub: string;
}[] = []; }[] = [];
@ -272,7 +272,10 @@ export async function getHistory(
// FIXME: what to do here?? // FIXME: what to do here??
continue; continue;
} }
const d = await tx.get(Stores.denominations, [c.exchangeBaseUrl, c.denomPub]); const d = await tx.get(Stores.denominations, [
c.exchangeBaseUrl,
c.denomPub,
]);
if (!d) { if (!d) {
// FIXME: what to do here?? // FIXME: what to do here??
continue; continue;
@ -286,7 +289,9 @@ export async function getHistory(
verboseDetails = { coins }; verboseDetails = { coins };
} }
const amountPaidWithFees = Amounts.sum( const amountPaidWithFees = Amounts.sum(
purchase.payReq.coins.map(x => Amounts.parseOrThrow(x.contribution)), purchase.payReq.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
),
).amount; ).amount;
history.push({ history.push({
type: HistoryEventType.PaymentSent, type: HistoryEventType.PaymentSent,
@ -301,7 +306,7 @@ export async function getHistory(
}); });
}); });
await tx.iter(Stores.refreshGroups).forEachAsync(async rg => { await tx.iter(Stores.refreshGroups).forEachAsync(async (rg) => {
if (!rg.timestampFinished) { if (!rg.timestampFinished) {
return; return;
} }
@ -340,7 +345,7 @@ export async function getHistory(
if (historyQuery?.extraDebug) { if (historyQuery?.extraDebug) {
const outputCoins: { const outputCoins: {
value: string; value: string;
denomPub: string, denomPub: string;
}[] = []; }[] = [];
for (const rs of rg.refreshSessionPerCoin) { for (const rs of rg.refreshSessionPerCoin) {
if (!rs) { if (!rs) {
@ -350,7 +355,10 @@ export async function getHistory(
if (!nd) { if (!nd) {
continue; continue;
} }
const d = await tx.get(Stores.denominations, [rs.exchangeBaseUrl, nd]); const d = await tx.get(Stores.denominations, [
rs.exchangeBaseUrl,
nd,
]);
if (!d) { if (!d) {
continue; continue;
} }
@ -362,7 +370,7 @@ export async function getHistory(
} }
verboseDetails = { verboseDetails = {
outputCoins: outputCoins, outputCoins: outputCoins,
} };
} }
history.push({ history.push({
type: HistoryEventType.Refreshed, type: HistoryEventType.Refreshed,
@ -379,7 +387,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async ru => { tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async (ru) => {
const reserve = await tx.get(Stores.reserves, ru.reservePub); const reserve = await tx.get(Stores.reserves, ru.reservePub);
if (!reserve) { if (!reserve) {
return; return;
@ -413,7 +421,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.tips).forEach(tip => { tx.iter(Stores.tips).forEach((tip) => {
if (tip.acceptedTimestamp) { if (tip.acceptedTimestamp) {
history.push({ history.push({
type: HistoryEventType.TipAccepted, type: HistoryEventType.TipAccepted,
@ -425,7 +433,7 @@ export async function getHistory(
} }
}); });
tx.iter(Stores.refundEvents).forEachAsync(async re => { tx.iter(Stores.refundEvents).forEachAsync(async (re) => {
const proposal = await tx.get(Stores.proposals, re.proposalId); const proposal = await tx.get(Stores.proposals, re.proposalId);
if (!proposal) { if (!proposal) {
return; return;
@ -486,7 +494,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.recoupGroups).forEach(rg => { tx.iter(Stores.recoupGroups).forEach((rg) => {
if (rg.timestampFinished) { if (rg.timestampFinished) {
let verboseDetails: any = undefined; let verboseDetails: any = undefined;
if (historyQuery?.extraDebug) { if (historyQuery?.extraDebug) {
@ -498,7 +506,10 @@ export async function getHistory(
history.push({ history.push({
type: HistoryEventType.FundsRecouped, type: HistoryEventType.FundsRecouped,
timestamp: rg.timestampFinished, timestamp: rg.timestampFinished,
eventId: makeEventId(HistoryEventType.FundsRecouped, rg.recoupGroupId), eventId: makeEventId(
HistoryEventType.FundsRecouped,
rg.recoupGroupId,
),
numCoinsRecouped: rg.coinPubs.length, numCoinsRecouped: rg.coinPubs.length,
verboseDetails, verboseDetails,
}); });

View File

@ -461,7 +461,7 @@ async function recordConfirmPay(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.coins, Stores.purchases, Stores.proposals, Stores.refreshGroups], [Stores.coins, Stores.purchases, Stores.proposals, Stores.refreshGroups],
async tx => { async (tx) => {
const p = await tx.get(Stores.proposals, proposal.proposalId); const p = await tx.get(Stores.proposals, proposal.proposalId);
if (p) { if (p) {
p.proposalStatus = ProposalStatus.ACCEPTED; p.proposalStatus = ProposalStatus.ACCEPTED;
@ -486,7 +486,9 @@ async function recordConfirmPay(
coin.currentAmount = remaining.amount; coin.currentAmount = remaining.amount;
await tx.put(Stores.coins, coin); await tx.put(Stores.coins, coin);
} }
const refreshCoinPubs = coinSelection.coinPubs.map(x => ({ coinPub: x })); const refreshCoinPubs = coinSelection.coinPubs.map((x) => ({
coinPub: x,
}));
await createRefreshGroup(tx, refreshCoinPubs, RefreshReason.Pay); await createRefreshGroup(tx, refreshCoinPubs, RefreshReason.Pay);
}, },
); );
@ -560,7 +562,7 @@ export async function abortFailedPayment(
RefundReason.AbortRefund, RefundReason.AbortRefund,
); );
await ws.db.runWithWriteTransaction([Stores.purchases], async tx => { await ws.db.runWithWriteTransaction([Stores.purchases], async (tx) => {
const p = await tx.get(Stores.purchases, proposalId); const p = await tx.get(Stores.purchases, proposalId);
if (!p) { if (!p) {
return; return;
@ -575,7 +577,7 @@ async function incrementProposalRetry(
proposalId: string, proposalId: string,
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.proposals], async tx => { await ws.db.runWithWriteTransaction([Stores.proposals], async (tx) => {
const pr = await tx.get(Stores.proposals, proposalId); const pr = await tx.get(Stores.proposals, proposalId);
if (!pr) { if (!pr) {
return; return;
@ -597,7 +599,7 @@ async function incrementPurchasePayRetry(
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
console.log("incrementing purchase pay retry with error", err); console.log("incrementing purchase pay retry with error", err);
await ws.db.runWithWriteTransaction([Stores.purchases], async tx => { await ws.db.runWithWriteTransaction([Stores.purchases], async (tx) => {
const pr = await tx.get(Stores.purchases, proposalId); const pr = await tx.get(Stores.purchases, proposalId);
if (!pr) { if (!pr) {
return; return;
@ -630,7 +632,7 @@ async function resetDownloadProposalRetry(
ws: InternalWalletState, ws: InternalWalletState,
proposalId: string, proposalId: string,
) { ) {
await ws.db.mutate(Stores.proposals, proposalId, x => { await ws.db.mutate(Stores.proposals, proposalId, (x) => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
} }
@ -685,7 +687,7 @@ async function processDownloadProposalImpl(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.proposals, Stores.purchases], [Stores.proposals, Stores.purchases],
async tx => { async (tx) => {
const p = await tx.get(Stores.proposals, proposalId); const p = await tx.get(Stores.proposals, proposalId);
if (!p) { if (!p) {
return; return;
@ -715,11 +717,11 @@ async function processDownloadProposalImpl(
payDeadline: parsedContractTerms.pay_deadline, payDeadline: parsedContractTerms.pay_deadline,
refundDeadline: parsedContractTerms.refund_deadline, refundDeadline: parsedContractTerms.refund_deadline,
wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1, wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1,
allowedAuditors: parsedContractTerms.auditors.map(x => ({ allowedAuditors: parsedContractTerms.auditors.map((x) => ({
auditorBaseUrl: x.url, auditorBaseUrl: x.url,
auditorPub: x.master_pub, auditorPub: x.master_pub,
})), })),
allowedExchanges: parsedContractTerms.exchanges.map(x => ({ allowedExchanges: parsedContractTerms.exchanges.map((x) => ({
exchangeBaseUrl: x.url, exchangeBaseUrl: x.url,
exchangePub: x.master_pub, exchangePub: x.master_pub,
})), })),
@ -797,7 +799,7 @@ async function startDownloadProposal(
downloadSessionId: sessionId, downloadSessionId: sessionId,
}; };
await ws.db.runWithWriteTransaction([Stores.proposals], async tx => { await ws.db.runWithWriteTransaction([Stores.proposals], async (tx) => {
const existingRecord = await tx.getIndexed( const existingRecord = await tx.getIndexed(
Stores.proposals.urlAndOrderIdIndex, Stores.proposals.urlAndOrderIdIndex,
[merchantBaseUrl, orderId], [merchantBaseUrl, orderId],
@ -878,7 +880,7 @@ export async function submitPay(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.purchases, Stores.payEvents], [Stores.purchases, Stores.payEvents],
async tx => { async (tx) => {
await tx.put(Stores.purchases, purchase); await tx.put(Stores.purchases, purchase);
const payEvent: PayEventRecord = { const payEvent: PayEventRecord = {
proposalId, proposalId,
@ -984,7 +986,7 @@ export async function preparePayForUri(
console.log( console.log(
"automatically re-submitting payment with different session ID", "automatically re-submitting payment with different session ID",
); );
await ws.db.runWithWriteTransaction([Stores.purchases], async tx => { await ws.db.runWithWriteTransaction([Stores.purchases], async (tx) => {
const p = await tx.get(Stores.purchases, proposalId); const p = await tx.get(Stores.purchases, proposalId);
if (!p) { if (!p) {
return; return;
@ -1035,7 +1037,7 @@ export async function confirmPay(
sessionIdOverride != purchase.lastSessionId sessionIdOverride != purchase.lastSessionId
) { ) {
logger.trace(`changing session ID to ${sessionIdOverride}`); logger.trace(`changing session ID to ${sessionIdOverride}`);
await ws.db.mutate(Stores.purchases, purchase.proposalId, x => { await ws.db.mutate(Stores.purchases, purchase.proposalId, (x) => {
x.lastSessionId = sessionIdOverride; x.lastSessionId = sessionIdOverride;
x.paymentSubmitPending = true; x.paymentSubmitPending = true;
return x; return x;
@ -1118,7 +1120,7 @@ async function resetPurchasePayRetry(
ws: InternalWalletState, ws: InternalWalletState,
proposalId: string, proposalId: string,
) { ) {
await ws.db.mutate(Stores.purchases, proposalId, x => { await ws.db.mutate(Stores.purchases, proposalId, (x) => {
if (x.payRetryInfo.active) { if (x.payRetryInfo.active) {
x.payRetryInfo = initRetryInfo(); x.payRetryInfo = initRetryInfo();
} }
@ -1151,7 +1153,7 @@ export async function refuseProposal(
) { ) {
const success = await ws.db.runWithWriteTransaction( const success = await ws.db.runWithWriteTransaction(
[Stores.proposals], [Stores.proposals],
async tx => { async (tx) => {
const proposal = await tx.get(Stores.proposals, proposalId); const proposal = await tx.get(Stores.proposals, proposalId);
if (!proposal) { if (!proposal) {
logger.trace(`proposal ${proposalId} not found, won't refuse proposal`); logger.trace(`proposal ${proposalId} not found, won't refuse proposal`);

View File

@ -59,7 +59,7 @@ async function gatherExchangePending(
// FIXME: exchanges should also be updated regularly // FIXME: exchanges should also be updated regularly
return; return;
} }
await tx.iter(Stores.exchanges).forEach(e => { await tx.iter(Stores.exchanges).forEach((e) => {
switch (e.updateStatus) { switch (e.updateStatus) {
case ExchangeUpdateStatus.Finished: case ExchangeUpdateStatus.Finished:
if (e.lastError) { if (e.lastError) {
@ -148,7 +148,7 @@ async function gatherReservePending(
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
// FIXME: this should be optimized by using an index for "onlyDue==true". // FIXME: this should be optimized by using an index for "onlyDue==true".
await tx.iter(Stores.reserves).forEach(reserve => { await tx.iter(Stores.reserves).forEach((reserve) => {
const reserveType = reserve.bankWithdrawStatusUrl ? "taler-bank" : "manual"; const reserveType = reserve.bankWithdrawStatusUrl ? "taler-bank" : "manual";
if (!reserve.retryInfo.active) { if (!reserve.retryInfo.active) {
return; return;
@ -214,7 +214,7 @@ async function gatherRefreshPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.refreshGroups).forEach(r => { await tx.iter(Stores.refreshGroups).forEach((r) => {
if (r.timestampFinished) { if (r.timestampFinished) {
return; return;
} }
@ -243,7 +243,7 @@ async function gatherWithdrawalPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.withdrawalSession).forEach(wsr => { await tx.iter(Stores.withdrawalSession).forEach((wsr) => {
if (wsr.timestampFinish) { if (wsr.timestampFinish) {
return; return;
} }
@ -277,7 +277,7 @@ async function gatherProposalPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.proposals).forEach(proposal => { await tx.iter(Stores.proposals).forEach((proposal) => {
if (proposal.proposalStatus == ProposalStatus.PROPOSED) { if (proposal.proposalStatus == ProposalStatus.PROPOSED) {
if (onlyDue) { if (onlyDue) {
return; return;
@ -318,7 +318,7 @@ async function gatherTipPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.tips).forEach(tip => { await tx.iter(Stores.tips).forEach((tip) => {
if (tip.pickedUp) { if (tip.pickedUp) {
return; return;
} }
@ -348,7 +348,7 @@ async function gatherPurchasePending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.purchases).forEach(pr => { await tx.iter(Stores.purchases).forEach((pr) => {
if (pr.paymentSubmitPending) { if (pr.paymentSubmitPending) {
resp.nextRetryDelay = updateRetryDelay( resp.nextRetryDelay = updateRetryDelay(
resp.nextRetryDelay, resp.nextRetryDelay,
@ -411,7 +411,7 @@ async function gatherRecoupPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.recoupGroups).forEach(rg => { await tx.iter(Stores.recoupGroups).forEach((rg) => {
if (rg.timestampFinished) { if (rg.timestampFinished) {
return; return;
} }
@ -450,7 +450,7 @@ export async function getPendingOperations(
Stores.purchases, Stores.purchases,
Stores.recoupGroups, Stores.recoupGroups,
], ],
async tx => { async (tx) => {
const walletBalance = await getBalancesInsideTransaction(ws, tx); const walletBalance = await getBalancesInsideTransaction(ws, tx);
const resp: PendingOperationsResponse = { const resp: PendingOperationsResponse = {
nextRetryDelay: { d_ms: Number.MAX_SAFE_INTEGER }, nextRetryDelay: { d_ms: Number.MAX_SAFE_INTEGER },

View File

@ -68,7 +68,7 @@ export function getTotalRefreshCost(
const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms); const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms);
const resultingAmount = Amounts.add( const resultingAmount = Amounts.add(
Amounts.getZero(withdrawAmount.currency), Amounts.getZero(withdrawAmount.currency),
...withdrawDenoms.map(d => d.value), ...withdrawDenoms.map((d) => d.value),
).amount; ).amount;
const totalCost = Amounts.sub(amountLeft, resultingAmount).amount; const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
logger.trace( logger.trace(
@ -139,7 +139,7 @@ async function refreshCreateSession(
); );
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.coins, Stores.refreshGroups], [Stores.coins, Stores.refreshGroups],
async tx => { async (tx) => {
const rg = await tx.get(Stores.refreshGroups, refreshGroupId); const rg = await tx.get(Stores.refreshGroups, refreshGroupId);
if (!rg) { if (!rg) {
return; return;
@ -175,7 +175,7 @@ async function refreshCreateSession(
// coin in the same transaction. // coin in the same transaction.
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.refreshGroups, Stores.coins], [Stores.refreshGroups, Stores.coins],
async tx => { async (tx) => {
const c = await tx.get(Stores.coins, coin.coinPub); const c = await tx.get(Stores.coins, coin.coinPub);
if (!c) { if (!c) {
throw Error("coin not found, but marked for refresh"); throw Error("coin not found, but marked for refresh");
@ -274,7 +274,7 @@ async function refreshMelt(
refreshSession.norevealIndex = norevealIndex; refreshSession.norevealIndex = norevealIndex;
await ws.db.mutate(Stores.refreshGroups, refreshGroupId, rg => { await ws.db.mutate(Stores.refreshGroups, refreshGroupId, (rg) => {
const rs = rg.refreshSessionPerCoin[coinIndex]; const rs = rg.refreshSessionPerCoin[coinIndex];
if (!rs) { if (!rs) {
return; return;
@ -420,7 +420,7 @@ async function refreshReveal(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.coins, Stores.refreshGroups], [Stores.coins, Stores.refreshGroups],
async tx => { async (tx) => {
const rg = await tx.get(Stores.refreshGroups, refreshGroupId); const rg = await tx.get(Stores.refreshGroups, refreshGroupId);
if (!rg) { if (!rg) {
console.log("no refresh session found"); console.log("no refresh session found");
@ -464,7 +464,7 @@ async function incrementRefreshRetry(
refreshGroupId: string, refreshGroupId: string,
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.refreshGroups], async tx => { await ws.db.runWithWriteTransaction([Stores.refreshGroups], async (tx) => {
const r = await tx.get(Stores.refreshGroups, refreshGroupId); const r = await tx.get(Stores.refreshGroups, refreshGroupId);
if (!r) { if (!r) {
return; return;
@ -499,7 +499,7 @@ async function resetRefreshGroupRetry(
ws: InternalWalletState, ws: InternalWalletState,
refreshSessionId: string, refreshSessionId: string,
) { ) {
await ws.db.mutate(Stores.refreshGroups, refreshSessionId, x => { await ws.db.mutate(Stores.refreshGroups, refreshSessionId, (x) => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
} }
@ -578,13 +578,13 @@ export async function createRefreshGroup(
const refreshGroup: RefreshGroupRecord = { const refreshGroup: RefreshGroupRecord = {
timestampFinished: undefined, timestampFinished: undefined,
finishedPerCoin: oldCoinPubs.map(x => false), finishedPerCoin: oldCoinPubs.map((x) => false),
lastError: undefined, lastError: undefined,
lastErrorPerCoin: {}, lastErrorPerCoin: {},
oldCoinPubs: oldCoinPubs.map(x => x.coinPub), oldCoinPubs: oldCoinPubs.map((x) => x.coinPub),
reason, reason,
refreshGroupId, refreshGroupId,
refreshSessionPerCoin: oldCoinPubs.map(x => undefined), refreshSessionPerCoin: oldCoinPubs.map((x) => undefined),
retryInfo: initRetryInfo(), retryInfo: initRetryInfo(),
}; };

View File

@ -65,7 +65,7 @@ import { getTimestampNow } from "../util/time";
const logger = new Logger("reserves.ts"); const logger = new Logger("reserves.ts");
async function resetReserveRetry(ws: InternalWalletState, reservePub: string) { async function resetReserveRetry(ws: InternalWalletState, reservePub: string) {
await ws.db.mutate(Stores.reserves, reservePub, x => { await ws.db.mutate(Stores.reserves, reservePub, (x) => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
} }
@ -156,7 +156,7 @@ export async function createReserve(
const resp = await ws.db.runWithWriteTransaction( const resp = await ws.db.runWithWriteTransaction(
[Stores.currencies, Stores.reserves, Stores.bankWithdrawUris], [Stores.currencies, Stores.reserves, Stores.bankWithdrawUris],
async tx => { async (tx) => {
// Check if we have already created a reserve for that bankWithdrawStatusUrl // Check if we have already created a reserve for that bankWithdrawStatusUrl
if (reserveRecord.bankWithdrawStatusUrl) { if (reserveRecord.bankWithdrawStatusUrl) {
const bwi = await tx.get( const bwi = await tx.get(
@ -194,7 +194,7 @@ export async function createReserve(
// Asynchronously process the reserve, but return // Asynchronously process the reserve, but return
// to the caller already. // to the caller already.
processReserve(ws, resp.reservePub, true).catch(e => { processReserve(ws, resp.reservePub, true).catch((e) => {
console.error("Processing reserve (after createReserve) failed:", e); console.error("Processing reserve (after createReserve) failed:", e);
}); });
@ -208,7 +208,7 @@ export async function forceQueryReserve(
ws: InternalWalletState, ws: InternalWalletState,
reservePub: string, reservePub: string,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.reserves], async tx => { await ws.db.runWithWriteTransaction([Stores.reserves], async (tx) => {
const reserve = await tx.get(Stores.reserves, reservePub); const reserve = await tx.get(Stores.reserves, reservePub);
if (!reserve) { if (!reserve) {
return; return;
@ -275,7 +275,7 @@ async function registerReserveWithBank(
reserve_pub: reservePub, reserve_pub: reservePub,
selected_exchange: reserve.exchangeWire, selected_exchange: reserve.exchangeWire,
}); });
await ws.db.mutate(Stores.reserves, reservePub, r => { await ws.db.mutate(Stores.reserves, reservePub, (r) => {
switch (r.reserveStatus) { switch (r.reserveStatus) {
case ReserveRecordStatus.REGISTERING_BANK: case ReserveRecordStatus.REGISTERING_BANK:
case ReserveRecordStatus.WAIT_CONFIRM_BANK: case ReserveRecordStatus.WAIT_CONFIRM_BANK:
@ -349,7 +349,7 @@ async function processReserveBankStatusImpl(
} }
if (status.transfer_done) { if (status.transfer_done) {
await ws.db.mutate(Stores.reserves, reservePub, r => { await ws.db.mutate(Stores.reserves, reservePub, (r) => {
switch (r.reserveStatus) { switch (r.reserveStatus) {
case ReserveRecordStatus.REGISTERING_BANK: case ReserveRecordStatus.REGISTERING_BANK:
case ReserveRecordStatus.WAIT_CONFIRM_BANK: case ReserveRecordStatus.WAIT_CONFIRM_BANK:
@ -365,7 +365,7 @@ async function processReserveBankStatusImpl(
}); });
await processReserveImpl(ws, reservePub, true); await processReserveImpl(ws, reservePub, true);
} else { } else {
await ws.db.mutate(Stores.reserves, reservePub, r => { await ws.db.mutate(Stores.reserves, reservePub, (r) => {
switch (r.reserveStatus) { switch (r.reserveStatus) {
case ReserveRecordStatus.WAIT_CONFIRM_BANK: case ReserveRecordStatus.WAIT_CONFIRM_BANK:
break; break;
@ -385,7 +385,7 @@ async function incrementReserveRetry(
reservePub: string, reservePub: string,
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.reserves], async tx => { await ws.db.runWithWriteTransaction([Stores.reserves], async (tx) => {
const r = await tx.get(Stores.reserves, reservePub); const r = await tx.get(Stores.reserves, reservePub);
if (!r) { if (!r) {
return; return;
@ -462,7 +462,7 @@ async function updateReserve(
const balance = Amounts.parseOrThrow(reserveInfo.balance); const balance = Amounts.parseOrThrow(reserveInfo.balance);
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.reserves, Stores.reserveUpdatedEvents], [Stores.reserves, Stores.reserveUpdatedEvents],
async tx => { async (tx) => {
const r = await tx.get(Stores.reserves, reservePub); const r = await tx.get(Stores.reserves, reservePub);
if (!r) { if (!r) {
return; return;
@ -501,8 +501,7 @@ async function updateReserve(
if (cmp == 0) { if (cmp == 0) {
// Nothing changed, go back to sleep! // Nothing changed, go back to sleep!
r.reserveStatus = ReserveRecordStatus.DORMANT; r.reserveStatus = ReserveRecordStatus.DORMANT;
} } else if (cmp > 0) {
else if (cmp > 0) {
const extra = Amounts.sub(balance, expectedBalance.amount).amount; const extra = Amounts.sub(balance, expectedBalance.amount).amount;
r.amountWithdrawRemaining = Amounts.add( r.amountWithdrawRemaining = Amounts.add(
r.amountWithdrawRemaining, r.amountWithdrawRemaining,
@ -591,7 +590,7 @@ export async function confirmReserve(
req: ConfirmReserveRequest, req: ConfirmReserveRequest,
): Promise<void> { ): Promise<void> {
const now = getTimestampNow(); const now = getTimestampNow();
await ws.db.mutate(Stores.reserves, req.reservePub, reserve => { await ws.db.mutate(Stores.reserves, req.reservePub, (reserve) => {
if (reserve.reserveStatus !== ReserveRecordStatus.UNCONFIRMED) { if (reserve.reserveStatus !== ReserveRecordStatus.UNCONFIRMED) {
return; return;
} }
@ -603,7 +602,7 @@ export async function confirmReserve(
ws.notify({ type: NotificationType.ReserveUpdated }); ws.notify({ type: NotificationType.ReserveUpdated });
processReserve(ws, req.reservePub, true).catch(e => { processReserve(ws, req.reservePub, true).catch((e) => {
console.log("processing reserve (after confirmReserve) failed:", e); console.log("processing reserve (after confirmReserve) failed:", e);
}); });
} }
@ -653,7 +652,7 @@ async function depleteReserve(
const withdrawalSessionId = encodeCrock(randomBytes(32)); const withdrawalSessionId = encodeCrock(randomBytes(32));
const totalCoinValue = Amounts.sum(denomsForWithdraw.map(x => x.value)) const totalCoinValue = Amounts.sum(denomsForWithdraw.map((x) => x.value))
.amount; .amount;
const withdrawalRecord: WithdrawalSessionRecord = { const withdrawalRecord: WithdrawalSessionRecord = {
@ -665,9 +664,9 @@ async function depleteReserve(
}, },
rawWithdrawalAmount: withdrawAmount, rawWithdrawalAmount: withdrawAmount,
timestampStart: getTimestampNow(), timestampStart: getTimestampNow(),
denoms: denomsForWithdraw.map(x => x.denomPub), denoms: denomsForWithdraw.map((x) => x.denomPub),
withdrawn: denomsForWithdraw.map(x => false), withdrawn: denomsForWithdraw.map((x) => false),
planchets: denomsForWithdraw.map(x => undefined), planchets: denomsForWithdraw.map((x) => undefined),
totalCoinValue, totalCoinValue,
retryInfo: initRetryInfo(), retryInfo: initRetryInfo(),
lastErrorPerCoin: {}, lastErrorPerCoin: {},
@ -675,7 +674,7 @@ async function depleteReserve(
}; };
const totalCoinWithdrawFee = Amounts.sum( const totalCoinWithdrawFee = Amounts.sum(
denomsForWithdraw.map(x => x.feeWithdraw), denomsForWithdraw.map((x) => x.feeWithdraw),
).amount; ).amount;
const totalWithdrawAmount = Amounts.add(totalCoinValue, totalCoinWithdrawFee) const totalWithdrawAmount = Amounts.add(totalCoinValue, totalCoinWithdrawFee)
.amount; .amount;
@ -706,7 +705,7 @@ async function depleteReserve(
const success = await ws.db.runWithWriteTransaction( const success = await ws.db.runWithWriteTransaction(
[Stores.withdrawalSession, Stores.reserves], [Stores.withdrawalSession, Stores.reserves],
async tx => { async (tx) => {
const myReserve = await tx.get(Stores.reserves, reservePub); const myReserve = await tx.get(Stores.reserves, reservePub);
if (!myReserve) { if (!myReserve) {
return false; return false;

View File

@ -15,10 +15,7 @@
*/ */
import { HttpRequestLibrary } from "../util/http"; import { HttpRequestLibrary } from "../util/http";
import { import { NextUrlResult, WalletBalance } from "../types/walletTypes";
NextUrlResult,
WalletBalance,
} from "../types/walletTypes";
import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi"; import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi";
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo"; import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo";
import { Logger } from "../util/logging"; import { Logger } from "../util/logging";

View File

@ -16,10 +16,7 @@
import { InternalWalletState } from "./state"; import { InternalWalletState } from "./state";
import { parseTipUri } from "../util/taleruri"; import { parseTipUri } from "../util/taleruri";
import { import { TipStatus, OperationError } from "../types/walletTypes";
TipStatus,
OperationError,
} from "../types/walletTypes";
import { import {
TipPickupGetResponse, TipPickupGetResponse,
TipPlanchetDetail, TipPlanchetDetail,
@ -130,7 +127,7 @@ async function incrementTipRetry(
refreshSessionId: string, refreshSessionId: string,
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.tips], async tx => { await ws.db.runWithWriteTransaction([Stores.tips], async (tx) => {
const t = await tx.get(Stores.tips, refreshSessionId); const t = await tx.get(Stores.tips, refreshSessionId);
if (!t) { if (!t) {
return; return;
@ -162,7 +159,7 @@ async function resetTipRetry(
ws: InternalWalletState, ws: InternalWalletState,
tipId: string, tipId: string,
): Promise<void> { ): Promise<void> {
await ws.db.mutate(Stores.tips, tipId, x => { await ws.db.mutate(Stores.tips, tipId, (x) => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
} }
@ -197,10 +194,10 @@ async function processTipImpl(
); );
const planchets = await Promise.all( const planchets = await Promise.all(
denomsForWithdraw.map(d => ws.cryptoApi.createTipPlanchet(d)), denomsForWithdraw.map((d) => ws.cryptoApi.createTipPlanchet(d)),
); );
await ws.db.mutate(Stores.tips, tipId, r => { await ws.db.mutate(Stores.tips, tipId, (r) => {
if (!r.planchets) { if (!r.planchets) {
r.planchets = planchets; r.planchets = planchets;
} }
@ -220,7 +217,7 @@ async function processTipImpl(
console.log("got planchets for tip!"); console.log("got planchets for tip!");
// Planchets in the form that the merchant expects // Planchets in the form that the merchant expects
const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map(p => ({ const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
coin_ev: p.coinEv, coin_ev: p.coinEv,
denom_pub_hash: p.denomPubHash, denom_pub_hash: p.denomPubHash,
})); }));
@ -269,7 +266,7 @@ async function processTipImpl(
const withdrawalSessionId = encodeCrock(getRandomBytes(32)); const withdrawalSessionId = encodeCrock(getRandomBytes(32));
const withdrawalSession: WithdrawalSessionRecord = { const withdrawalSession: WithdrawalSessionRecord = {
denoms: planchets.map(x => x.denomPub), denoms: planchets.map((x) => x.denomPub),
exchangeBaseUrl: tipRecord.exchangeUrl, exchangeBaseUrl: tipRecord.exchangeUrl,
planchets: planchets, planchets: planchets,
source: { source: {
@ -279,8 +276,8 @@ async function processTipImpl(
timestampStart: getTimestampNow(), timestampStart: getTimestampNow(),
withdrawSessionId: withdrawalSessionId, withdrawSessionId: withdrawalSessionId,
rawWithdrawalAmount: tipRecord.amount, rawWithdrawalAmount: tipRecord.amount,
withdrawn: planchets.map(x => false), withdrawn: planchets.map((x) => false),
totalCoinValue: Amounts.sum(planchets.map(p => p.coinValue)).amount, totalCoinValue: Amounts.sum(planchets.map((p) => p.coinValue)).amount,
lastErrorPerCoin: {}, lastErrorPerCoin: {},
retryInfo: initRetryInfo(), retryInfo: initRetryInfo(),
timestampFinish: undefined, timestampFinish: undefined,
@ -289,7 +286,7 @@ async function processTipImpl(
await ws.db.runWithWriteTransaction( await ws.db.runWithWriteTransaction(
[Stores.tips, Stores.withdrawalSession], [Stores.tips, Stores.withdrawalSession],
async tx => { async (tx) => {
const tr = await tx.get(Stores.tips, tipId); const tr = await tx.get(Stores.tips, tipId);
if (!tr) { if (!tr) {
return; return;

View File

@ -144,7 +144,7 @@ async function getPossibleDenoms(
): Promise<DenominationRecord[]> { ): Promise<DenominationRecord[]> {
return await ws.db return await ws.db
.iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl) .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl)
.filter(d => { .filter((d) => {
return ( return (
(d.status === DenominationStatus.Unverified || (d.status === DenominationStatus.Unverified ||
d.status === DenominationStatus.VerifiedGood) && d.status === DenominationStatus.VerifiedGood) &&
@ -248,7 +248,7 @@ async function processPlanchet(
const success = await ws.db.runWithWriteTransaction( const success = await ws.db.runWithWriteTransaction(
[Stores.coins, Stores.withdrawalSession, Stores.reserves], [Stores.coins, Stores.withdrawalSession, Stores.reserves],
async tx => { async (tx) => {
const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId); const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
if (!ws) { if (!ws) {
return false; return false;
@ -429,17 +429,20 @@ async function makePlanchet(
reservePub: r.reservePub, reservePub: r.reservePub,
withdrawSig: r.withdrawSig, withdrawSig: r.withdrawSig,
}; };
await ws.db.runWithWriteTransaction([Stores.withdrawalSession], async tx => { await ws.db.runWithWriteTransaction(
const myWs = await tx.get(Stores.withdrawalSession, withdrawalSessionId); [Stores.withdrawalSession],
if (!myWs) { async (tx) => {
return; const myWs = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
} if (!myWs) {
if (myWs.planchets[coinIndex]) { return;
return; }
} if (myWs.planchets[coinIndex]) {
myWs.planchets[coinIndex] = newPlanchet; return;
await tx.put(Stores.withdrawalSession, myWs); }
}); myWs.planchets[coinIndex] = newPlanchet;
await tx.put(Stores.withdrawalSession, myWs);
},
);
} }
async function processWithdrawCoin( async function processWithdrawCoin(
@ -483,19 +486,22 @@ async function incrementWithdrawalRetry(
withdrawalSessionId: string, withdrawalSessionId: string,
err: OperationError | undefined, err: OperationError | undefined,
): Promise<void> { ): Promise<void> {
await ws.db.runWithWriteTransaction([Stores.withdrawalSession], async tx => { await ws.db.runWithWriteTransaction(
const wsr = await tx.get(Stores.withdrawalSession, withdrawalSessionId); [Stores.withdrawalSession],
if (!wsr) { async (tx) => {
return; const wsr = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
} if (!wsr) {
if (!wsr.retryInfo) { return;
return; }
} if (!wsr.retryInfo) {
wsr.retryInfo.retryCounter++; return;
updateRetryInfoTimeout(wsr.retryInfo); }
wsr.lastError = err; wsr.retryInfo.retryCounter++;
await tx.put(Stores.withdrawalSession, wsr); updateRetryInfoTimeout(wsr.retryInfo);
}); wsr.lastError = err;
await tx.put(Stores.withdrawalSession, wsr);
},
);
ws.notify({ type: NotificationType.WithdrawOperationError }); ws.notify({ type: NotificationType.WithdrawOperationError });
} }
@ -516,7 +522,7 @@ async function resetWithdrawSessionRetry(
ws: InternalWalletState, ws: InternalWalletState,
withdrawalSessionId: string, withdrawalSessionId: string,
) { ) {
await ws.db.mutate(Stores.withdrawalSession, withdrawalSessionId, x => { await ws.db.mutate(Stores.withdrawalSession, withdrawalSessionId, (x) => {
if (x.retryInfo.active) { if (x.retryInfo.active) {
x.retryInfo = initRetryInfo(); x.retryInfo = initRetryInfo();
} }
@ -594,12 +600,14 @@ export async function getExchangeWithdrawalInfo(
const possibleDenoms = await ws.db const possibleDenoms = await ws.db
.iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl) .iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl)
.filter(d => d.isOffered); .filter((d) => d.isOffered);
const trustedAuditorPubs = []; const trustedAuditorPubs = [];
const currencyRecord = await ws.db.get(Stores.currencies, amount.currency); const currencyRecord = await ws.db.get(Stores.currencies, amount.currency);
if (currencyRecord) { if (currencyRecord) {
trustedAuditorPubs.push(...currencyRecord.auditors.map(a => a.auditorPub)); trustedAuditorPubs.push(
...currencyRecord.auditors.map((a) => a.auditorPub),
);
} }
let versionMatch; let versionMatch;

View File

@ -18,9 +18,9 @@
* Type and schema definitions for the wallet's history. * Type and schema definitions for the wallet's history.
*/ */
/** /**
* Imports. * Imports.
*/ */
import { RefreshReason } from "./walletTypes"; import { RefreshReason } from "./walletTypes";
import { ReserveTransaction } from "./ReserveTransaction"; import { ReserveTransaction } from "./ReserveTransaction";
import { WithdrawalSource } from "./dbTypes"; import { WithdrawalSource } from "./dbTypes";

View File

@ -24,7 +24,7 @@ const amt = (
currency: string, currency: string,
): Amounts.AmountJson => ({ value, fraction, currency }); ): Amounts.AmountJson => ({ value, fraction, currency });
test("amount addition (simple)", t => { test("amount addition (simple)", (t) => {
const a1 = amt(1, 0, "EUR"); const a1 = amt(1, 0, "EUR");
const a2 = amt(1, 0, "EUR"); const a2 = amt(1, 0, "EUR");
const a3 = amt(2, 0, "EUR"); const a3 = amt(2, 0, "EUR");
@ -32,14 +32,14 @@ test("amount addition (simple)", t => {
t.pass(); t.pass();
}); });
test("amount addition (saturation)", t => { test("amount addition (saturation)", (t) => {
const a1 = amt(1, 0, "EUR"); const a1 = amt(1, 0, "EUR");
const res = Amounts.add(amt(Amounts.maxAmountValue, 0, "EUR"), a1); const res = Amounts.add(amt(Amounts.maxAmountValue, 0, "EUR"), a1);
t.true(res.saturated); t.true(res.saturated);
t.pass(); t.pass();
}); });
test("amount subtraction (simple)", t => { test("amount subtraction (simple)", (t) => {
const a1 = amt(2, 5, "EUR"); const a1 = amt(2, 5, "EUR");
const a2 = amt(1, 0, "EUR"); const a2 = amt(1, 0, "EUR");
const a3 = amt(1, 5, "EUR"); const a3 = amt(1, 5, "EUR");
@ -47,7 +47,7 @@ test("amount subtraction (simple)", t => {
t.pass(); t.pass();
}); });
test("amount subtraction (saturation)", t => { test("amount subtraction (saturation)", (t) => {
const a1 = amt(0, 0, "EUR"); const a1 = amt(0, 0, "EUR");
const a2 = amt(1, 0, "EUR"); const a2 = amt(1, 0, "EUR");
let res = Amounts.sub(a1, a2); let res = Amounts.sub(a1, a2);
@ -57,7 +57,7 @@ test("amount subtraction (saturation)", t => {
t.pass(); t.pass();
}); });
test("amount comparison", t => { test("amount comparison", (t) => {
t.is(Amounts.cmp(amt(1, 0, "EUR"), amt(1, 0, "EUR")), 0); t.is(Amounts.cmp(amt(1, 0, "EUR"), amt(1, 0, "EUR")), 0);
t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 0, "EUR")), 1); t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 0, "EUR")), 1);
t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 2, "EUR")), -1); t.is(Amounts.cmp(amt(1, 1, "EUR"), amt(1, 2, "EUR")), -1);
@ -68,7 +68,7 @@ test("amount comparison", t => {
t.pass(); t.pass();
}); });
test("amount parsing", t => { test("amount parsing", (t) => {
t.is( t.is(
Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0"), amt(0, 0, "TESTKUDOS")), Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0"), amt(0, 0, "TESTKUDOS")),
0, 0,
@ -117,7 +117,7 @@ test("amount parsing", t => {
t.pass(); t.pass();
}); });
test("amount stringification", t => { test("amount stringification", (t) => {
t.is(Amounts.toString(amt(0, 0, "TESTKUDOS")), "TESTKUDOS:0"); t.is(Amounts.toString(amt(0, 0, "TESTKUDOS")), "TESTKUDOS:0");
t.is(Amounts.toString(amt(4, 94000000, "TESTKUDOS")), "TESTKUDOS:4.94"); t.is(Amounts.toString(amt(4, 94000000, "TESTKUDOS")), "TESTKUDOS:4.94");
t.is(Amounts.toString(amt(0, 10000000, "TESTKUDOS")), "TESTKUDOS:0.1"); t.is(Amounts.toString(amt(0, 10000000, "TESTKUDOS")), "TESTKUDOS:0.1");
@ -128,7 +128,7 @@ test("amount stringification", t => {
t.pass(); t.pass();
}); });
test("contract terms validation", t => { test("contract terms validation", (t) => {
const c = { const c = {
nonce: "123123123", nonce: "123123123",
h_wire: "123", h_wire: "123",

View File

@ -317,7 +317,6 @@ export class ReturnCoinsRequest {
static checked: (obj: any) => ReturnCoinsRequest; static checked: (obj: any) => ReturnCoinsRequest;
} }
/** /**
* Status of processing a tip. * Status of processing a tip.
*/ */

View File

@ -21,7 +21,12 @@
/** /**
* Imports. * Imports.
*/ */
import { getTimestampNow, Timestamp, timestampSubtractDuraction, timestampDifference } from "../util/time"; import {
getTimestampNow,
Timestamp,
timestampSubtractDuraction,
timestampDifference,
} from "../util/time";
/** /**
* Maximum request per second, per origin. * Maximum request per second, per origin.
@ -38,7 +43,6 @@ const MAX_PER_MINUTE = 100;
*/ */
const MAX_PER_HOUR = 1000; const MAX_PER_HOUR = 1000;
/** /**
* Throttling state for one origin. * Throttling state for one origin.
*/ */
@ -52,12 +56,21 @@ class OriginState {
const now = getTimestampNow(); const now = getTimestampNow();
const d = timestampDifference(now, this.lastUpdate); const d = timestampDifference(now, this.lastUpdate);
if (d.d_ms === "forever") { if (d.d_ms === "forever") {
throw Error("assertion failed") throw Error("assertion failed");
} }
const d_s = d.d_ms / 1000; const d_s = d.d_ms / 1000;
this.tokensSecond = Math.min(MAX_PER_SECOND, this.tokensSecond + (d_s / 1000)); this.tokensSecond = Math.min(
this.tokensMinute = Math.min(MAX_PER_MINUTE, this.tokensMinute + (d_s / 1000 * 60)); MAX_PER_SECOND,
this.tokensHour = Math.min(MAX_PER_HOUR, this.tokensHour + (d_s / 1000 * 60 * 60)); this.tokensSecond + d_s / 1000,
);
this.tokensMinute = Math.min(
MAX_PER_MINUTE,
this.tokensMinute + (d_s / 1000) * 60,
);
this.tokensHour = Math.min(
MAX_PER_HOUR,
this.tokensHour + (d_s / 1000) * 60 * 60,
);
this.lastUpdate = now; this.lastUpdate = now;
} }
@ -104,7 +117,7 @@ export class RequestThrottler {
if (s) { if (s) {
return s; return s;
} }
const ns = this.perOriginInfo[origin] = new OriginState(); const ns = (this.perOriginInfo[origin] = new OriginState());
return ns; return ns;
} }

View File

@ -40,9 +40,9 @@ export class AsyncOpMemoMap<T> {
// Wrap the operation in case it immediately throws // Wrap the operation in case it immediately throws
const p = Promise.resolve().then(() => pg()); const p = Promise.resolve().then(() => pg());
this.memoMap[key] = { this.memoMap[key] = {
p, p,
n, n,
t: new Date().getTime(), t: new Date().getTime(),
}; };
return p.finally(() => { return p.finally(() => {
this.cleanUp(key, n); this.cleanUp(key, n);
@ -53,7 +53,6 @@ export class AsyncOpMemoMap<T> {
} }
} }
export class AsyncOpMemoSingle<T> { export class AsyncOpMemoSingle<T> {
private n = 0; private n = 0;
private memoEntry: MemoEntry<T> | undefined; private memoEntry: MemoEntry<T> | undefined;

View File

@ -19,7 +19,13 @@
*/ */
import test from "ava"; import test from "ava";
import { Codec, makeCodecForObject, makeCodecForConstString, codecForString, makeCodecForUnion } from "./codec"; import {
Codec,
makeCodecForObject,
makeCodecForConstString,
codecForString,
makeCodecForUnion,
} from "./codec";
interface MyObj { interface MyObj {
foo: string; foo: string;
@ -37,7 +43,7 @@ interface AltTwo {
type MyUnion = AltOne | AltTwo; type MyUnion = AltOne | AltTwo;
test("basic codec", t => { test("basic codec", (t) => {
const myObjCodec = makeCodecForObject<MyObj>() const myObjCodec = makeCodecForObject<MyObj>()
.property("foo", codecForString) .property("foo", codecForString)
.build("MyObj"); .build("MyObj");
@ -49,7 +55,7 @@ test("basic codec", t => {
}); });
}); });
test("union", t => { test("union", (t) => {
const altOneCodec: Codec<AltOne> = makeCodecForObject<AltOne>() const altOneCodec: Codec<AltOne> = makeCodecForObject<AltOne>()
.property("type", makeCodecForConstString("one")) .property("type", makeCodecForConstString("one"))
.property("foo", codecForString) .property("foo", codecForString)

View File

@ -110,7 +110,8 @@ class ObjectCodecBuilder<OutputType, PartialOutputType> {
throw new DecodingError( throw new DecodingError(
`expected object for ${objectDisplayName} at ${renderContext( `expected object for ${objectDisplayName} at ${renderContext(
c, c,
)} but got ${typeof x}`) )} but got ${typeof x}`,
);
} }
const obj: any = {}; const obj: any = {};
for (const prop of propList) { for (const prop of propList) {
@ -273,7 +274,9 @@ export const codecForNumber: Codec<number> = {
if (typeof x === "number") { if (typeof x === "number") {
return x; return x;
} }
throw new DecodingError(`expected number at ${renderContext(c)} but got ${typeof x}`); throw new DecodingError(
`expected number at ${renderContext(c)} but got ${typeof x}`,
);
}, },
}; };
@ -285,7 +288,9 @@ export const codecForBoolean: Codec<boolean> = {
if (typeof x === "boolean") { if (typeof x === "boolean") {
return x; return x;
} }
throw new DecodingError(`expected boolean at ${renderContext(c)} but got ${typeof x}`); throw new DecodingError(
`expected boolean at ${renderContext(c)} but got ${typeof x}`,
);
}, },
}; };
@ -297,7 +302,9 @@ export const codecForString: Codec<string> = {
if (typeof x === "string") { if (typeof x === "string") {
return x; return x;
} }
throw new DecodingError(`expected string at ${renderContext(c)} but got ${typeof x}`); throw new DecodingError(
`expected string at ${renderContext(c)} but got ${typeof x}`,
);
}, },
}; };
@ -320,21 +327,25 @@ export function makeCodecForConstString<V extends string>(s: V): Codec<V> {
return x; return x;
} }
throw new DecodingError( throw new DecodingError(
`expected string constant "${s}" at ${renderContext(c)} but got ${typeof x}`, `expected string constant "${s}" at ${renderContext(
c,
)} but got ${typeof x}`,
); );
}, },
}; };
} }
export function makeCodecOptional<V>(innerCodec: Codec<V>): Codec<V | undefined> { export function makeCodecOptional<V>(
innerCodec: Codec<V>,
): Codec<V | undefined> {
return { return {
decode(x: any, c?: Context): V | undefined { decode(x: any, c?: Context): V | undefined {
if (x === undefined || x === null) { if (x === undefined || x === null) {
return undefined; return undefined;
} }
return innerCodec.decode(x, c); return innerCodec.decode(x, c);
} },
} };
} }
export function typecheckedCodec<T = undefined>(c: Codec<T>): Codec<T> { export function typecheckedCodec<T = undefined>(c: Codec<T>): Codec<T> {

View File

@ -14,25 +14,26 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import test from "ava"; import test from "ava";
import * as helpers from "./helpers"; import * as helpers from "./helpers";
test("URL canonicalization", (t) => { test("URL canonicalization", (t) => {
// converts to relative, adds https // converts to relative, adds https
t.is( t.is(
"https://alice.example.com/exchange/", "https://alice.example.com/exchange/",
helpers.canonicalizeBaseUrl("alice.example.com/exchange")); helpers.canonicalizeBaseUrl("alice.example.com/exchange"),
);
// keeps http, adds trailing slash // keeps http, adds trailing slash
t.is( t.is(
"http://alice.example.com/exchange/", "http://alice.example.com/exchange/",
helpers.canonicalizeBaseUrl("http://alice.example.com/exchange")); helpers.canonicalizeBaseUrl("http://alice.example.com/exchange"),
);
// keeps http, adds trailing slash // keeps http, adds trailing slash
t.is( t.is(
"http://alice.example.com/exchange/", "http://alice.example.com/exchange/",
helpers.canonicalizeBaseUrl("http://alice.example.com/exchange#foobar")); helpers.canonicalizeBaseUrl("http://alice.example.com/exchange#foobar"),
);
t.pass(); t.pass();
}); });

View File

@ -34,7 +34,6 @@ export function amountToPretty(amount: AmountJson): string {
return `${x} ${amount.currency}`; return `${x} ${amount.currency}`;
} }
/** /**
* Canonicalize a base url, typically for the exchange. * Canonicalize a base url, typically for the exchange.
* *
@ -53,7 +52,6 @@ export function canonicalizeBaseUrl(url: string) {
return x.href; return x.href;
} }
/** /**
* Convert object to JSON with canonical ordering of keys * Convert object to JSON with canonical ordering of keys
* and whitespace omitted. * and whitespace omitted.
@ -84,7 +82,6 @@ export function canonicalJson(obj: any): string {
return s + "}"; return s + "}";
} }
/** /**
* Check for deep equality of two objects. * Check for deep equality of two objects.
* Only arrays, objects and primitives are supported. * Only arrays, objects and primitives are supported.
@ -99,11 +96,12 @@ export function deepEquals(x: any, y: any): boolean {
} }
const p = Object.keys(x); const p = Object.keys(x);
return Object.keys(y).every((i) => p.indexOf(i) !== -1) && return (
p.every((i) => deepEquals(x[i], y[i])); Object.keys(y).every((i) => p.indexOf(i) !== -1) &&
p.every((i) => deepEquals(x[i], y[i]))
);
} }
/** /**
* Map from a collection to a list or results and then * Map from a collection to a list or results and then
* concatenate the results. * concatenate the results.
@ -125,12 +123,11 @@ export function hash(val: any): number {
} }
/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
* integers. Since we want the results to be always positive, convert the * integers. Since we want the results to be always positive, convert the
* signed int to an unsigned by doing an unsigned bitshift. */ * signed int to an unsigned by doing an unsigned bitshift. */
return h >>> 0; return h >>> 0;
} }
/** /**
* Lexically compare two strings. * Lexically compare two strings.
*/ */

View File

@ -101,12 +101,12 @@ export class BrowserHttpLib implements HttpRequestLibrary {
myRequest.send(); myRequest.send();
} }
myRequest.onerror = e => { myRequest.onerror = (e) => {
console.error("http request error"); console.error("http request error");
reject(Error("could not make XMLHttpRequest")); reject(Error("could not make XMLHttpRequest"));
}; };
myRequest.addEventListener("readystatechange", e => { myRequest.addEventListener("readystatechange", (e) => {
if (myRequest.readyState === XMLHttpRequest.DONE) { if (myRequest.readyState === XMLHttpRequest.DONE) {
if (myRequest.status === 0) { if (myRequest.status === 0) {
reject( reject(
@ -134,7 +134,7 @@ export class BrowserHttpLib implements HttpRequestLibrary {
// Create a map of header names to values // Create a map of header names to values
const headerMap = new Headers(); const headerMap = new Headers();
arr.forEach(function(line) { arr.forEach(function (line) {
const parts = line.split(": "); const parts = line.split(": ");
const header = parts.shift(); const header = parts.shift();
const value = parts.join(": "); const value = parts.join(": ");

View File

@ -19,12 +19,30 @@ import * as LibtoolVersion from "./libtoolVersion";
import test from "ava"; import test from "ava";
test("version comparison", (t) => { test("version comparison", (t) => {
t.deepEqual(LibtoolVersion.compare("0:0:0", "0:0:0"), {compatible: true, currentCmp: 0}); t.deepEqual(LibtoolVersion.compare("0:0:0", "0:0:0"), {
compatible: true,
currentCmp: 0,
});
t.deepEqual(LibtoolVersion.compare("0:0:0", ""), undefined); t.deepEqual(LibtoolVersion.compare("0:0:0", ""), undefined);
t.deepEqual(LibtoolVersion.compare("foo", "0:0:0"), undefined); t.deepEqual(LibtoolVersion.compare("foo", "0:0:0"), undefined);
t.deepEqual(LibtoolVersion.compare("0:0:0", "1:0:1"), {compatible: true, currentCmp: -1}); t.deepEqual(LibtoolVersion.compare("0:0:0", "1:0:1"), {
t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:1"), {compatible: true, currentCmp: -1}); compatible: true,
t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:0"), {compatible: false, currentCmp: -1}); currentCmp: -1,
t.deepEqual(LibtoolVersion.compare("1:0:0", "0:5:0"), {compatible: false, currentCmp: 1}); });
t.deepEqual(LibtoolVersion.compare("1:0:1", "1:5:1"), {compatible: true, currentCmp: 0}); t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:1"), {
compatible: true,
currentCmp: -1,
});
t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:0"), {
compatible: false,
currentCmp: -1,
});
t.deepEqual(LibtoolVersion.compare("1:0:0", "0:5:0"), {
compatible: false,
currentCmp: 1,
});
t.deepEqual(LibtoolVersion.compare("1:0:1", "1:5:1"), {
compatible: true,
currentCmp: 0,
});
}); });

View File

@ -19,7 +19,6 @@
* See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html * See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
*/ */
/** /**
* Result of comparing two libtool versions. * Result of comparing two libtool versions.
*/ */
@ -44,7 +43,10 @@ interface Version {
/** /**
* Compare two libtool-style version strings. * Compare two libtool-style version strings.
*/ */
export function compare(me: string, other: string): VersionMatchResult|undefined { export function compare(
me: string,
other: string,
): VersionMatchResult | undefined {
const meVer = parseVersion(me); const meVer = parseVersion(me);
const otherVer = parseVersion(other); const otherVer = parseVersion(other);
@ -52,16 +54,16 @@ export function compare(me: string, other: string): VersionMatchResult|undefined
return undefined; return undefined;
} }
const compatible = (meVer.current - meVer.age <= otherVer.current && const compatible =
meVer.current >= (otherVer.current - otherVer.age)); meVer.current - meVer.age <= otherVer.current &&
meVer.current >= otherVer.current - otherVer.age;
const currentCmp = Math.sign(meVer.current - otherVer.current); const currentCmp = Math.sign(meVer.current - otherVer.current);
return {compatible, currentCmp}; return { compatible, currentCmp };
} }
function parseVersion(v: string): Version | undefined {
function parseVersion(v: string): Version|undefined {
const [currentStr, revisionStr, ageStr, ...rest] = v.split(":"); const [currentStr, revisionStr, ageStr, ...rest] = v.split(":");
if (rest.length !== 0) { if (rest.length !== 0) {
return undefined; return undefined;
@ -82,5 +84,5 @@ function parseVersion(v: string): Version|undefined {
return undefined; return undefined;
} }
return {current, revision, age}; return { current, revision, age };
} }

View File

@ -20,9 +20,8 @@ interface PaytoUri {
params: { [name: string]: string }; params: { [name: string]: string };
} }
export function parsePaytoUri(s: string): PaytoUri | undefined { export function parsePaytoUri(s: string): PaytoUri | undefined {
const pfx = "payto://" const pfx = "payto://";
if (!s.startsWith(pfx)) { if (!s.startsWith(pfx)) {
return undefined; return undefined;
} }
@ -50,5 +49,5 @@ export function parsePaytoUri(s: string): PaytoUri | undefined {
targetPath, targetPath,
targetType, targetType,
params, params,
} };
} }

View File

@ -14,11 +14,11 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
export interface OpenedPromise<T> { export interface OpenedPromise<T> {
promise: Promise<T>; promise: Promise<T>;
resolve: (val: T) => void; resolve: (val: T) => void;
reject: (err: any) => void; reject: (err: any) => void;
} }
/** /**
* Get an unresolved promise together with its extracted resolve / reject * Get an unresolved promise together with its extracted resolve / reject

View File

@ -271,11 +271,14 @@ export class TransactionHandle {
return new ResultStream<T>(req); return new ResultStream<T>(req);
} }
iterIndexed<S extends IDBValidKey,T>( iterIndexed<S extends IDBValidKey, T>(
index: Index<S, T>, index: Index<S, T>,
key?: any, key?: any,
): ResultStream<T> { ): ResultStream<T> {
const req = this.tx.objectStore(index.storeName).index(index.indexName).openCursor(key); const req = this.tx
.objectStore(index.storeName)
.index(index.indexName)
.openCursor(key);
return new ResultStream<T>(req); return new ResultStream<T>(req);
} }
@ -298,7 +301,7 @@ function runWithTransaction<T>(
): Promise<T> { ): Promise<T> {
const stack = Error("Failed transaction was started here."); const stack = Error("Failed transaction was started here.");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const storeName = stores.map(x => x.name); const storeName = stores.map((x) => x.name);
const tx = db.transaction(storeName, mode); const tx = db.transaction(storeName, mode);
let funResult: any = undefined; let funResult: any = undefined;
let gotFunResult: boolean = false; let gotFunResult: boolean = false;
@ -332,11 +335,11 @@ function runWithTransaction<T>(
const th = new TransactionHandle(tx); const th = new TransactionHandle(tx);
const resP = Promise.resolve().then(() => f(th)); const resP = Promise.resolve().then(() => f(th));
resP resP
.then(result => { .then((result) => {
gotFunResult = true; gotFunResult = true;
funResult = result; funResult = result;
}) })
.catch(e => { .catch((e) => {
if (e == TransactionAbort) { if (e == TransactionAbort) {
console.info("aborting transaction"); console.info("aborting transaction");
} else { } else {
@ -344,7 +347,8 @@ function runWithTransaction<T>(
console.error(stack); console.error(stack);
tx.abort(); tx.abort();
} }
}).catch((e) => { })
.catch((e) => {
console.error("fatal: aborting transaction failed", e); console.error("fatal: aborting transaction failed", e);
}); });
}); });
@ -394,15 +398,19 @@ export function openDatabase(
databaseName: string, databaseName: string,
databaseVersion: number, databaseVersion: number,
onVersionChange: () => void, onVersionChange: () => void,
onUpgradeNeeded: (db: IDBDatabase, oldVersion: number, newVersion: number) => void, onUpgradeNeeded: (
db: IDBDatabase,
oldVersion: number,
newVersion: number,
) => void,
): Promise<IDBDatabase> { ): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => { return new Promise<IDBDatabase>((resolve, reject) => {
const req = idbFactory.open(databaseName, databaseVersion); const req = idbFactory.open(databaseName, databaseVersion);
req.onerror = e => { req.onerror = (e) => {
console.log("taler database error", e); console.log("taler database error", e);
reject(new Error("database error")); reject(new Error("database error"));
}; };
req.onsuccess = e => { req.onsuccess = (e) => {
req.result.onversionchange = (evt: IDBVersionChangeEvent) => { req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
console.log( console.log(
`handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`, `handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`,
@ -412,7 +420,7 @@ export function openDatabase(
}; };
resolve(req.result); resolve(req.result);
}; };
req.onupgradeneeded = e => { req.onupgradeneeded = (e) => {
const db = req.result; const db = req.result;
onUpgradeNeeded(db, e.oldVersion, e.newVersion!); onUpgradeNeeded(db, e.oldVersion, e.newVersion!);
console.log( console.log(
@ -503,10 +511,7 @@ export class Database {
key: any, key: any,
): Promise<T | undefined> { ): Promise<T | undefined> {
const tx = this.db.transaction([index.storeName], "readonly"); const tx = this.db.transaction([index.storeName], "readonly");
const req = tx const req = tx.objectStore(index.storeName).index(index.indexName).get(key);
.objectStore(index.storeName)
.index(index.indexName)
.get(key);
const v = await requestToPromise(req); const v = await requestToPromise(req);
await transactionToPromise(tx); await transactionToPromise(tx);
return v; return v;

View File

@ -110,11 +110,13 @@ export class Configuration {
getString(section: string, option: string): ConfigValue<string> { getString(section: string, option: string): ConfigValue<string> {
const val = (this.sectionMap[section] ?? {})[option]; const val = (this.sectionMap[section] ?? {})[option];
return new ConfigValue(section, option, val, x => x); return new ConfigValue(section, option, val, (x) => x);
} }
getAmount(section: string, option: string): ConfigValue<AmountJson> { getAmount(section: string, option: string): ConfigValue<AmountJson> {
const val = (this.sectionMap[section] ?? {})[option]; const val = (this.sectionMap[section] ?? {})[option];
return new ConfigValue(section, option, val, x => Amounts.parseOrThrow(x)); return new ConfigValue(section, option, val, (x) =>
Amounts.parseOrThrow(x),
);
} }
} }

View File

@ -22,7 +22,7 @@ import {
parseTipUri, parseTipUri,
} from "./taleruri"; } from "./taleruri";
test("taler pay url parsing: wrong scheme", t => { test("taler pay url parsing: wrong scheme", (t) => {
const url1 = "talerfoo://"; const url1 = "talerfoo://";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
t.is(r1, undefined); t.is(r1, undefined);
@ -32,7 +32,7 @@ test("taler pay url parsing: wrong scheme", t => {
t.is(r2, undefined); t.is(r2, undefined);
}); });
test("taler pay url parsing: defaults", t => { test("taler pay url parsing: defaults", (t) => {
const url1 = "taler://pay/example.com/-/-/myorder"; const url1 = "taler://pay/example.com/-/-/myorder";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -52,7 +52,7 @@ test("taler pay url parsing: defaults", t => {
t.is(r2.sessionId, "mysession"); t.is(r2.sessionId, "mysession");
}); });
test("taler pay url parsing: trailing parts", t => { test("taler pay url parsing: trailing parts", (t) => {
const url1 = "taler://pay/example.com/-/-/myorder/mysession/spam/eggs"; const url1 = "taler://pay/example.com/-/-/myorder/mysession/spam/eggs";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -63,7 +63,7 @@ test("taler pay url parsing: trailing parts", t => {
t.is(r1.sessionId, "mysession"); t.is(r1.sessionId, "mysession");
}); });
test("taler pay url parsing: instance", t => { test("taler pay url parsing: instance", (t) => {
const url1 = "taler://pay/example.com/-/myinst/myorder"; const url1 = "taler://pay/example.com/-/myinst/myorder";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -74,7 +74,7 @@ test("taler pay url parsing: instance", t => {
t.is(r1.orderId, "myorder"); t.is(r1.orderId, "myorder");
}); });
test("taler pay url parsing: path prefix and instance", t => { test("taler pay url parsing: path prefix and instance", (t) => {
const url1 = "taler://pay/example.com/mypfx/myinst/myorder"; const url1 = "taler://pay/example.com/mypfx/myinst/myorder";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -84,7 +84,7 @@ test("taler pay url parsing: path prefix and instance", t => {
t.is(r1.merchantBaseUrl, "https://example.com/mypfx/instances/myinst/"); t.is(r1.merchantBaseUrl, "https://example.com/mypfx/instances/myinst/");
}); });
test("taler pay url parsing: complex path prefix", t => { test("taler pay url parsing: complex path prefix", (t) => {
const url1 = "taler://pay/example.com/mypfx%2Fpublic/-/myorder"; const url1 = "taler://pay/example.com/mypfx%2Fpublic/-/myorder";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -96,7 +96,7 @@ test("taler pay url parsing: complex path prefix", t => {
t.is(r1.sessionId, undefined); t.is(r1.sessionId, undefined);
}); });
test("taler pay uri parsing: complex path prefix and instance", t => { test("taler pay uri parsing: complex path prefix and instance", (t) => {
const url1 = "taler://pay/example.com/mypfx%2Fpublic/foo/myorder"; const url1 = "taler://pay/example.com/mypfx%2Fpublic/foo/myorder";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -107,7 +107,7 @@ test("taler pay uri parsing: complex path prefix and instance", t => {
t.is(r1.orderId, "myorder"); t.is(r1.orderId, "myorder");
}); });
test("taler refund uri parsing: non-https #1", t => { test("taler refund uri parsing: non-https #1", (t) => {
const url1 = "taler://refund/example.com/-/-/myorder?insecure=1"; const url1 = "taler://refund/example.com/-/-/myorder?insecure=1";
const r1 = parseRefundUri(url1); const r1 = parseRefundUri(url1);
if (!r1) { if (!r1) {
@ -115,10 +115,10 @@ test("taler refund uri parsing: non-https #1", t => {
return; return;
} }
t.is(r1.merchantBaseUrl, "http://example.com/public/"); t.is(r1.merchantBaseUrl, "http://example.com/public/");
t.is(r1.orderId, "myorder") t.is(r1.orderId, "myorder");
}); });
test("taler pay uri parsing: non-https #1", t => { test("taler pay uri parsing: non-https #1", (t) => {
const url1 = "taler://pay/example.com/-/-/myorder?insecure=1"; const url1 = "taler://pay/example.com/-/-/myorder?insecure=1";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -126,10 +126,10 @@ test("taler pay uri parsing: non-https #1", t => {
return; return;
} }
t.is(r1.merchantBaseUrl, "http://example.com/public/"); t.is(r1.merchantBaseUrl, "http://example.com/public/");
t.is(r1.orderId, "myorder") t.is(r1.orderId, "myorder");
}); });
test("taler pay url parsing: non-https #2", t => { test("taler pay url parsing: non-https #2", (t) => {
const url1 = "taler://pay/example.com/-/-/myorder?insecure=2"; const url1 = "taler://pay/example.com/-/-/myorder?insecure=2";
const r1 = parsePayUri(url1); const r1 = parsePayUri(url1);
if (!r1) { if (!r1) {
@ -140,7 +140,7 @@ test("taler pay url parsing: non-https #2", t => {
t.is(r1.orderId, "myorder"); t.is(r1.orderId, "myorder");
}); });
test("taler withdraw uri parsing", t => { test("taler withdraw uri parsing", (t) => {
const url1 = "taler://withdraw/bank.example.com/-/12345"; const url1 = "taler://withdraw/bank.example.com/-/12345";
const r1 = parseWithdrawUri(url1); const r1 = parseWithdrawUri(url1);
if (!r1) { if (!r1) {
@ -150,7 +150,7 @@ test("taler withdraw uri parsing", t => {
t.is(r1.statusUrl, "https://bank.example.com/api/withdraw-operation/12345"); t.is(r1.statusUrl, "https://bank.example.com/api/withdraw-operation/12345");
}); });
test("taler refund uri parsing", t => { test("taler refund uri parsing", (t) => {
const url1 = "taler://refund/merchant.example.com/-/-/1234"; const url1 = "taler://refund/merchant.example.com/-/-/1234";
const r1 = parseRefundUri(url1); const r1 = parseRefundUri(url1);
if (!r1) { if (!r1) {
@ -161,7 +161,7 @@ test("taler refund uri parsing", t => {
t.is(r1.orderId, "1234"); t.is(r1.orderId, "1234");
}); });
test("taler refund uri parsing with instance", t => { test("taler refund uri parsing with instance", (t) => {
const url1 = "taler://refund/merchant.example.com/-/myinst/1234"; const url1 = "taler://refund/merchant.example.com/-/myinst/1234";
const r1 = parseRefundUri(url1); const r1 = parseRefundUri(url1);
if (!r1) { if (!r1) {
@ -169,23 +169,23 @@ test("taler refund uri parsing with instance", t => {
return; return;
} }
t.is(r1.orderId, "1234"); t.is(r1.orderId, "1234");
t.is(r1.merchantBaseUrl, "https://merchant.example.com/public/instances/myinst/"); t.is(
r1.merchantBaseUrl,
"https://merchant.example.com/public/instances/myinst/",
);
}); });
test("taler tip pickup uri", t => { test("taler tip pickup uri", (t) => {
const url1 = "taler://tip/merchant.example.com/-/-/tipid"; const url1 = "taler://tip/merchant.example.com/-/-/tipid";
const r1 = parseTipUri(url1); const r1 = parseTipUri(url1);
if (!r1) { if (!r1) {
t.fail(); t.fail();
return; return;
} }
t.is( t.is(r1.merchantBaseUrl, "https://merchant.example.com/public/");
r1.merchantBaseUrl,
"https://merchant.example.com/public/",
);
}); });
test("taler tip pickup uri with instance", t => { test("taler tip pickup uri with instance", (t) => {
const url1 = "taler://tip/merchant.example.com/-/tipm/tipid"; const url1 = "taler://tip/merchant.example.com/-/tipm/tipid";
const r1 = parseTipUri(url1); const r1 = parseTipUri(url1);
if (!r1) { if (!r1) {
@ -199,7 +199,7 @@ test("taler tip pickup uri with instance", t => {
t.is(r1.merchantTipId, "tipid"); t.is(r1.merchantTipId, "tipid");
}); });
test("taler tip pickup uri with instance and prefix", t => { test("taler tip pickup uri with instance and prefix", (t) => {
const url1 = "taler://tip/merchant.example.com/my%2fpfx/tipm/tipid"; const url1 = "taler://tip/merchant.example.com/my%2fpfx/tipm/tipid";
const r1 = parseTipUri(url1); const r1 = parseTipUri(url1);
if (!r1) { if (!r1) {

View File

@ -138,7 +138,11 @@ export function timestampDifference(t1: Timestamp, t2: Timestamp): Duration {
return { d_ms: Math.abs(t1.t_ms - t2.t_ms) }; return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
} }
export function timestampIsBetween(t: Timestamp, start: Timestamp, end: Timestamp) { export function timestampIsBetween(
t: Timestamp,
start: Timestamp,
end: Timestamp,
) {
if (timestampCmp(t, start) < 0) { if (timestampCmp(t, start) < 0) {
return false; return false;
} }

View File

@ -31,8 +31,7 @@ export interface TimerHandle {
} }
class IntervalHandle { class IntervalHandle {
constructor(public h: any) { constructor(public h: any) {}
}
clear() { clear() {
clearInterval(this.h); clearInterval(this.h);
@ -40,8 +39,7 @@ class IntervalHandle {
} }
class TimeoutHandle { class TimeoutHandle {
constructor(public h: any) { constructor(public h: any) {}
}
clear() { clear() {
clearTimeout(this.h); clearTimeout(this.h);
@ -78,7 +76,6 @@ export function after(delayMs: number, callback: () => void): TimerHandle {
return new TimeoutHandle(setTimeout(callback, delayMs)); return new TimeoutHandle(setTimeout(callback, delayMs));
} }
const nullTimerHandle = { const nullTimerHandle = {
clear() { clear() {
// do nothing // do nothing

View File

@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* Display and manipulate wire information. * Display and manipulate wire information.
* *
@ -50,4 +49,3 @@ export function summarizeWire(w: any): string {
return i18n.str`Unknown Wire Detail`; return i18n.str`Unknown Wire Detail`;
} }
} }

View File

@ -640,7 +640,9 @@ export class Wallet {
* Accept a refund, return the contract hash for the contract * Accept a refund, return the contract hash for the contract
* that was involved in the refund. * that was involved in the refund.
*/ */
async applyRefund(talerRefundUri: string): Promise<{ contractTermsHash: string }> { async applyRefund(
talerRefundUri: string,
): Promise<{ contractTermsHash: string }> {
return applyRefund(this.ws, talerRefundUri); return applyRefund(this.ws, talerRefundUri);
} }

View File

@ -1,11 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<script src="../../dist/background-bundle.js"></script> <script src="../../dist/background-bundle.js"></script>
<title>(wallet bg page)</title> <title>(wallet bg page)</title>
</head> </head>
<body> <body>
<img id="taler-logo" src="/img/icon.png"> <img id="taler-logo" src="/img/icon.png" />
</body> </body>
</html> </html>

View File

@ -14,10 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { isFirefox } from "./compat"; import { isFirefox } from "./compat";
/** /**
* Polyfill for requestAnimationFrame, which * Polyfill for requestAnimationFrame, which
* doesn't work from a background page. * doesn't work from a background page.
@ -28,7 +26,6 @@ function rAF(cb: (ts: number) => void) {
}, 100 /* 100 ms delay between frames */); }, 100 /* 100 ms delay between frames */);
} }
/** /**
* Badge for Chrome that renders a Taler logo with a rotating ring if some * Badge for Chrome that renders a Taler logo with a rotating ring if some
* background activity is happening. * background activity is happening.
@ -139,16 +136,27 @@ export class ChromeBadge {
if (this.animationRunning) { if (this.animationRunning) {
/* Draw circle around the "T" with an opening of this.gapWidth */ /* Draw circle around the "T" with an opening of this.gapWidth */
const aMax = ChromeBadge.rotationAngleMax; const aMax = ChromeBadge.rotationAngleMax;
const startAngle = this.rotationAngle / aMax * Math.PI * 2; const startAngle = (this.rotationAngle / aMax) * Math.PI * 2;
const stopAngle = ((this.rotationAngle + aMax - this.gapWidth) / aMax) * Math.PI * 2; const stopAngle =
this.ctx.arc(0, 0, this.canvas.width / 2 - 2, /* radius */ startAngle, stopAngle, false); ((this.rotationAngle + aMax - this.gapWidth) / aMax) * Math.PI * 2;
this.ctx.arc(
0,
0,
this.canvas.width / 2 - 2,
/* radius */ startAngle,
stopAngle,
false,
);
} else { } else {
/* Draw full circle */ /* Draw full circle */
this.ctx.arc(0, 0, this.ctx.arc(
this.canvas.width / 2 - 2, /* radius */ 0,
0, 0,
Math.PI * 2, this.canvas.width / 2 - 2 /* radius */,
false); 0,
Math.PI * 2,
false,
);
} }
this.ctx.stroke(); this.ctx.stroke();
// go back to the origin // go back to the origin
@ -162,7 +170,14 @@ export class ChromeBadge {
const ch = this.canvas.height; const ch = this.canvas.height;
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.arc(cw - r, ch - r, r, 0, 2 * Math.PI, false); this.ctx.arc(cw - r, ch - r, r, 0, 2 * Math.PI, false);
const gradient = this.ctx.createRadialGradient(cw - r, ch - r, r, cw - r, ch - r, 5); const gradient = this.ctx.createRadialGradient(
cw - r,
ch - r,
r,
cw - r,
ch - r,
5,
);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)"); gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(1, "blue"); gradient.addColorStop(1, "blue");
this.ctx.fillStyle = gradient; this.ctx.fillStyle = gradient;
@ -173,11 +188,13 @@ export class ChromeBadge {
// tslint:disable-next-line:no-string-literal // tslint:disable-next-line:no-string-literal
if (window["chrome"] && window.chrome["browserAction"]) { if (window["chrome"] && window.chrome["browserAction"]) {
try { try {
const imageData = this.ctx.getImageData(0, const imageData = this.ctx.getImageData(
0, 0,
this.canvas.width, 0,
this.canvas.height); this.canvas.width,
chrome.browserAction.setIcon({imageData}); this.canvas.height,
);
chrome.browserAction.setIcon({ imageData });
} catch (e) { } catch (e) {
// Might fail if browser has over-eager canvas fingerprinting countermeasures. // Might fail if browser has over-eager canvas fingerprinting countermeasures.
// There's nothing we can do then ... // There's nothing we can do then ...
@ -194,7 +211,7 @@ export class ChromeBadge {
return; return;
} }
this.animationRunning = true; this.animationRunning = true;
let start: number|undefined; let start: number | undefined;
const step = (timestamp: number) => { const step = (timestamp: number) => {
if (!this.animationRunning) { if (!this.animationRunning) {
return; return;
@ -206,8 +223,10 @@ export class ChromeBadge {
// stop if we're close enough to origin // stop if we're close enough to origin
this.rotationAngle = 0; this.rotationAngle = 0;
} else { } else {
this.rotationAngle = (this.rotationAngle + (timestamp - start) * this.rotationAngle =
ChromeBadge.rotationSpeed) % ChromeBadge.rotationAngleMax; (this.rotationAngle +
(timestamp - start) * ChromeBadge.rotationSpeed) %
ChromeBadge.rotationAngleMax;
} }
if (this.isBusy) { if (this.isBusy) {
if (this.gapWidth < ChromeBadge.openMax) { if (this.gapWidth < ChromeBadge.openMax) {

View File

@ -20,16 +20,16 @@
*/ */
export function isFirefox(): boolean { export function isFirefox(): boolean {
const rt = chrome.runtime as any; const rt = chrome.runtime as any;
if (typeof rt.getBrowserInfo === "function") { if (typeof rt.getBrowserInfo === "function") {
return true; return true;
} }
return false; return false;
} }
/** /**
* Check if we are running under nodejs. * Check if we are running under nodejs.
*/ */
export function isNode() { export function isNode() {
return (typeof process !== 'undefined') && (process.release.name === 'node') return typeof process !== "undefined" && process.release.name === "node";
} }

View File

@ -21,19 +21,17 @@
/** /**
* Imports. * Imports.
*/ */
import {strings} from "../i18n/strings"; import { strings } from "../i18n/strings";
// @ts-ignore: no type decl for this library // @ts-ignore: no type decl for this library
import * as jedLib from "jed"; import * as jedLib from "jed";
import * as React from "react"; import * as React from "react";
const jed = setupJed(); const jed = setupJed();
let enableTracing = false; let enableTracing = false;
/** /**
* Set up jed library for internationalization, * Set up jed library for internationalization,
* based on browser language settings. * based on browser language settings.
@ -56,7 +54,6 @@ function setupJed(): any {
return new jedLib.Jed(strings[lang]); return new jedLib.Jed(strings[lang]);
} }
/** /**
* Convert template strings to a msgid * Convert template strings to a msgid
*/ */
@ -71,22 +68,22 @@ function toI18nString(stringSeq: ReadonlyArray<string>) {
return s; return s;
} }
/** /**
* Internationalize a string template with arbitrary serialized values. * Internationalize a string template with arbitrary serialized values.
*/ */
export function str(stringSeq: TemplateStringsArray, ...values: any[]) { export function str(stringSeq: TemplateStringsArray, ...values: any[]) {
const s = toI18nString(stringSeq); const s = toI18nString(stringSeq);
const tr = jed.translate(s).ifPlural(1, s).fetch(...values); const tr = jed
.translate(s)
.ifPlural(1, s)
.fetch(...values);
return tr; return tr;
} }
interface TranslateSwitchProps { interface TranslateSwitchProps {
target: number; target: number;
} }
function stringifyChildren(children: any): string { function stringifyChildren(children: any): string {
let n = 1; let n = 1;
const ss = React.Children.map(children, (c) => { const ss = React.Children.map(children, (c) => {
@ -100,7 +97,6 @@ function stringifyChildren(children: any): string {
return s; return s;
} }
interface TranslateProps { interface TranslateProps {
/** /**
* Component that the translated element should be wrapped in. * Component that the translated element should be wrapped in.
@ -114,7 +110,6 @@ interface TranslateProps {
wrapProps?: any; wrapProps?: any;
} }
/** /**
* Translate text node children of this component. * Translate text node children of this component.
* If a child component might produce a text node, it must be wrapped * If a child component might produce a text node, it must be wrapped
@ -130,11 +125,19 @@ interface TranslateProps {
export class Translate extends React.Component<TranslateProps, {}> { export class Translate extends React.Component<TranslateProps, {}> {
render(): JSX.Element { render(): JSX.Element {
const s = stringifyChildren(this.props.children); const s = stringifyChildren(this.props.children);
const tr = jed.ngettext(s, s, 1).split(/%(\d+)\$s/).filter((e: any, i: number) => i % 2 === 0); const tr = jed
.ngettext(s, s, 1)
.split(/%(\d+)\$s/)
.filter((e: any, i: number) => i % 2 === 0);
const childArray = React.Children.toArray(this.props.children!); const childArray = React.Children.toArray(this.props.children!);
for (let i = 0; i < childArray.length - 1; ++i) { for (let i = 0; i < childArray.length - 1; ++i) {
if ((typeof childArray[i]) === "string" && (typeof childArray[i + 1]) === "string") { if (
childArray[i + 1] = (childArray[i] as string).concat(childArray[i + 1] as string); typeof childArray[i] === "string" &&
typeof childArray[i + 1] === "string"
) {
childArray[i + 1] = (childArray[i] as string).concat(
childArray[i + 1] as string,
);
childArray.splice(i, 1); childArray.splice(i, 1);
} }
} }
@ -158,7 +161,6 @@ export class Translate extends React.Component<TranslateProps, {}> {
} }
} }
/** /**
* Switch translation based on singular or plural based on the target prop. * Switch translation based on singular or plural based on the target prop.
* Should only contain TranslateSingular and TransplatePlural as children. * Should only contain TranslateSingular and TransplatePlural as children.
@ -171,7 +173,10 @@ export class Translate extends React.Component<TranslateProps, {}> {
* </TranslateSwitch> * </TranslateSwitch>
* ``` * ```
*/ */
export class TranslateSwitch extends React.Component<TranslateSwitchProps, void> { export class TranslateSwitch extends React.Component<
TranslateSwitchProps,
void
> {
render(): JSX.Element { render(): JSX.Element {
let singular: React.ReactElement<TranslationPluralProps> | undefined; let singular: React.ReactElement<TranslationPluralProps> | undefined;
let plural: React.ReactElement<TranslationPluralProps> | undefined; let plural: React.ReactElement<TranslationPluralProps> | undefined;
@ -186,7 +191,7 @@ export class TranslateSwitch extends React.Component<TranslateSwitchProps, void>
} }
}); });
} }
if ((!singular) || (!plural)) { if (!singular || !plural) {
console.error("translation not found"); console.error("translation not found");
return React.createElement("span", {}, ["translation not found"]); return React.createElement("span", {}, ["translation not found"]);
} }
@ -198,7 +203,6 @@ export class TranslateSwitch extends React.Component<TranslateSwitchProps, void>
} }
} }
interface TranslationPluralProps { interface TranslationPluralProps {
target: number; target: number;
} }
@ -206,14 +210,24 @@ interface TranslationPluralProps {
/** /**
* See [[TranslateSwitch]]. * See [[TranslateSwitch]].
*/ */
export class TranslatePlural extends React.Component<TranslationPluralProps, void> { export class TranslatePlural extends React.Component<
TranslationPluralProps,
void
> {
render(): JSX.Element { render(): JSX.Element {
const s = stringifyChildren(this.props.children); const s = stringifyChildren(this.props.children);
const tr = jed.ngettext(s, s, 1).split(/%(\d+)\$s/).filter((e: any, i: number) => i % 2 === 0); const tr = jed
.ngettext(s, s, 1)
.split(/%(\d+)\$s/)
.filter((e: any, i: number) => i % 2 === 0);
const childArray = React.Children.toArray(this.props.children!); const childArray = React.Children.toArray(this.props.children!);
for (let i = 0; i < childArray.length - 1; ++i) { for (let i = 0; i < childArray.length - 1; ++i) {
if ((typeof childArray[i]) === "string" && (typeof childArray[i + 1]) === "string") { if (
childArray[i + i] = childArray[i] as string + childArray[i + 1] as string; typeof childArray[i] === "string" &&
typeof childArray[i + 1] === "string"
) {
childArray[i + i] = ((childArray[i] as string) +
childArray[i + 1]) as string;
childArray.splice(i, 1); childArray.splice(i, 1);
} }
} }
@ -234,18 +248,27 @@ export class TranslatePlural extends React.Component<TranslationPluralProps, voi
} }
} }
/** /**
* See [[TranslateSwitch]]. * See [[TranslateSwitch]].
*/ */
export class TranslateSingular extends React.Component<TranslationPluralProps, void> { export class TranslateSingular extends React.Component<
TranslationPluralProps,
void
> {
render(): JSX.Element { render(): JSX.Element {
const s = stringifyChildren(this.props.children); const s = stringifyChildren(this.props.children);
const tr = jed.ngettext(s, s, 1).split(/%(\d+)\$s/).filter((e: any, i: number) => i % 2 === 0); const tr = jed
.ngettext(s, s, 1)
.split(/%(\d+)\$s/)
.filter((e: any, i: number) => i % 2 === 0);
const childArray = React.Children.toArray(this.props.children!); const childArray = React.Children.toArray(this.props.children!);
for (let i = 0; i < childArray.length - 1; ++i) { for (let i = 0; i < childArray.length - 1; ++i) {
if ((typeof childArray[i]) === "string" && (typeof childArray[i + 1]) === "string") { if (
childArray[i + i] = childArray[i] as string + childArray[i + 1] as string; typeof childArray[i] === "string" &&
typeof childArray[i + 1] === "string"
) {
childArray[i + i] = ((childArray[i] as string) +
childArray[i + 1]) as string;
childArray.splice(i, 1); childArray.splice(i, 1);
} }
} }

View File

@ -147,7 +147,10 @@ export interface MessageMap {
response: walletTypes.BenchmarkResult; response: walletTypes.BenchmarkResult;
}; };
"get-withdraw-details": { "get-withdraw-details": {
request: { talerWithdrawUri: string; maybeSelectedExchange: string | undefined }; request: {
talerWithdrawUri: string;
maybeSelectedExchange: string | undefined;
};
response: walletTypes.WithdrawDetails; response: walletTypes.WithdrawDetails;
}; };
"accept-withdrawal": { "accept-withdrawal": {
@ -159,12 +162,11 @@ export interface MessageMap {
response: walletTypes.PreparePayResult; response: walletTypes.PreparePayResult;
}; };
"get-diagnostics": { "get-diagnostics": {
request: { }; request: {};
response: walletTypes.WalletDiagnostics; response: walletTypes.WalletDiagnostics;
}; };
} }
/** /**
* String literal types for messages. * String literal types for messages.
*/ */

View File

@ -41,18 +41,17 @@ if (document.documentElement.getAttribute("data-taler-nojs")) {
interface Handler { interface Handler {
type: string; type: string;
listener: (e: Event) => void|Promise<void>; listener: (e: Event) => void | Promise<void>;
} }
const handlers: Handler[] = []; const handlers: Handler[] = [];
let sheet: CSSStyleSheet | null;
let sheet: CSSStyleSheet|null;
function initStyle() { function initStyle() {
logVerbose && console.log("taking over styles"); logVerbose && console.log("taking over styles");
const name = "taler-presence-stylesheet"; const name = "taler-presence-stylesheet";
const content = "/* Taler stylesheet controlled by JS */"; const content = "/* Taler stylesheet controlled by JS */";
let style = document.getElementById(name) as HTMLStyleElement|null; let style = document.getElementById(name) as HTMLStyleElement | null;
if (!style) { if (!style) {
style = document.createElement("style"); style = document.createElement("style");
// Needed by WebKit // Needed by WebKit
@ -68,7 +67,9 @@ function initStyle() {
style.innerText = content; style.innerText = content;
} }
if (!style.sheet) { if (!style.sheet) {
throw Error("taler-presence-stylesheet should be a style sheet (<link> or <style>)"); throw Error(
"taler-presence-stylesheet should be a style sheet (<link> or <style>)",
);
} }
sheet = style.sheet as CSSStyleSheet; sheet = style.sheet as CSSStyleSheet;
while (sheet.cssRules.length > 0) { while (sheet.cssRules.length > 0) {
@ -77,7 +78,6 @@ function initStyle() {
} }
} }
function setStyles(installed: boolean) { function setStyles(installed: boolean) {
if (!sheet || !sheet.cssRules) { if (!sheet || !sheet.cssRules) {
return; return;
@ -93,7 +93,6 @@ function setStyles(installed: boolean) {
} }
} }
function onceOnComplete(cb: () => void) { function onceOnComplete(cb: () => void) {
if (document.readyState === "complete") { if (document.readyState === "complete") {
cb(); cb();
@ -106,7 +105,6 @@ function onceOnComplete(cb: () => void) {
} }
} }
function init() { function init() {
onceOnComplete(() => { onceOnComplete(() => {
if (document.documentElement.getAttribute("data-taler-nojs")) { if (document.documentElement.getAttribute("data-taler-nojs")) {
@ -131,7 +129,6 @@ function init() {
type HandlerFn = (detail: any, sendResponse: (msg: any) => void) => void; type HandlerFn = (detail: any, sendResponse: (msg: any) => void) => void;
function registerHandlers() { function registerHandlers() {
/** /**
* Add a handler for a DOM event, which automatically * Add a handler for a DOM event, which automatically
@ -149,12 +146,16 @@ function registerHandlers() {
} }
let callId: number | undefined; let callId: number | undefined;
let detail; let detail;
if ((e instanceof CustomEvent) && e.detail && e.detail.callId !== undefined) { if (
e instanceof CustomEvent &&
e.detail &&
e.detail.callId !== undefined
) {
callId = e.detail.callId; callId = e.detail.callId;
detail = e.detail; detail = e.detail;
} }
const responder = (msg?: any) => { const responder = (msg?: any) => {
const fullMsg = Object.assign({}, msg, {callId}); const fullMsg = Object.assign({}, msg, { callId });
let opts = { detail: fullMsg }; let opts = { detail: fullMsg };
if ("function" === typeof cloneInto) { if ("function" === typeof cloneInto) {
opts = cloneInto(opts, document.defaultView); opts = cloneInto(opts, document.defaultView);
@ -165,7 +166,7 @@ function registerHandlers() {
handler(detail, responder); handler(detail, responder);
}; };
document.addEventListener(type, handlerWrap); document.addEventListener(type, handlerWrap);
handlers.push({type, listener: handlerWrap}); handlers.push({ type, listener: handlerWrap });
} }
addHandler("taler-query-id", (msg: any, sendResponse: any) => { addHandler("taler-query-id", (msg: any, sendResponse: any) => {
@ -178,25 +179,34 @@ function registerHandlers() {
}); });
addHandler("taler-create-reserve", (msg: any) => { addHandler("taler-create-reserve", (msg: any) => {
const uri = new URL(chrome.extension.getURL("/src/webex/pages/confirm-create-reserve.html")); const uri = new URL(
chrome.extension.getURL("/src/webex/pages/confirm-create-reserve.html"),
);
uri.searchParams.set("amount", JSON.stringify(msg.amount)); uri.searchParams.set("amount", JSON.stringify(msg.amount));
uri.searchParams.set("bank_url", document.location.href); uri.searchParams.set("bank_url", document.location.href);
uri.searchParams.set("callback_url", new URL(msg.callback_url, document.location.href).href); uri.searchParams.set(
"callback_url",
new URL(msg.callback_url, document.location.href).href,
);
uri.searchParams.set("suggested_exchange_url", msg.suggested_exchange_url); uri.searchParams.set("suggested_exchange_url", msg.suggested_exchange_url);
uri.searchParams.set("wt_types", JSON.stringify(msg.wt_types)); uri.searchParams.set("wt_types", JSON.stringify(msg.wt_types));
window.location.href = uri.href; window.location.href = uri.href;
}); });
addHandler("taler-add-auditor", (msg: any) => { addHandler("taler-add-auditor", (msg: any) => {
const uri = new URL(chrome.extension.getURL("/src/webex/pages/add-auditor.html")); const uri = new URL(
uri.searchParams.set("req", JSON.stringify(msg)) chrome.extension.getURL("/src/webex/pages/add-auditor.html"),
);
uri.searchParams.set("req", JSON.stringify(msg));
window.location.href = uri.href; window.location.href = uri.href;
}); });
addHandler("taler-confirm-reserve", async (msg: any, sendResponse: any) => { addHandler("taler-confirm-reserve", async (msg: any, sendResponse: any) => {
const reservePub = msg.reserve_pub; const reservePub = msg.reserve_pub;
if (typeof reservePub !== "string") { if (typeof reservePub !== "string") {
console.error("taler-confirm-reserve expects parameter reserve_pub of type 'string'"); console.error(
"taler-confirm-reserve expects parameter reserve_pub of type 'string'",
);
return; return;
} }
await wxApi.confirmReserve(msg.reserve_pub); await wxApi.confirmReserve(msg.reserve_pub);

View File

@ -1,32 +1,32 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<head> <title>Taler Wallet: Add Auditor</title>
<meta charset="UTF-8">
<title>Taler Wallet: Add Auditor</title> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="icon" href="/img/icon.png" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/add-auditor-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> <style>
<script src="/dist/add-auditor-bundle.js"></script> .tree-item {
margin: 2em;
<style> border-radius: 5px;
.tree-item { border: 1px solid gray;
margin: 2em; padding: 1em;
border-radius: 5px; }
border: 1px solid gray; .button-linky {
padding: 1em; background: none;
} color: black;
.button-linky { text-decoration: underline;
background: none; border: none;
color: black; }
text-decoration: underline; </style>
border: none; </head>
}
</style>
<body> <body>
<div id="container"></div> <div id="container"></div>

View File

@ -35,8 +35,7 @@ interface ConfirmAuditorProps {
function ConfirmAuditor(props: ConfirmAuditorProps) { function ConfirmAuditor(props: ConfirmAuditorProps) {
const [addDone, setAddDone] = useState(false); const [addDone, setAddDone] = useState(false);
const add = async () => {
const add = async() => {
const currencies = await getCurrencies(); const currencies = await getCurrencies();
let currency: CurrencyRecord | undefined; let currency: CurrencyRecord | undefined;
@ -78,7 +77,7 @@ function ConfirmAuditor(props: ConfirmAuditorProps) {
await updateCurrency(currency); await updateCurrency(currency);
setAddDone(true); setAddDone(true);
} };
const back = () => { const back = () => {
window.history.back(); window.history.back();
@ -115,7 +114,6 @@ function ConfirmAuditor(props: ConfirmAuditorProps) {
); );
} }
registerMountPage(() => { registerMountPage(() => {
const walletPageUrl = new URL(document.location.href); const walletPageUrl = new URL(document.location.href);
const url = walletPageUrl.searchParams.get("url"); const url = walletPageUrl.searchParams.get("url");
@ -136,5 +134,5 @@ registerMountPage(() => {
} }
const expirationStamp = Number.parseInt(auditorStampStr); const expirationStamp = Number.parseInt(auditorStampStr);
const args = { url, currency, auditorPub, expirationStamp }; const args = { url, currency, auditorPub, expirationStamp };
return <ConfirmAuditor {...args}/>; return <ConfirmAuditor {...args} />;
}); });

View File

@ -1,34 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Auditors</title>
<head> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<meta charset="UTF-8">
<title>Taler Wallet: Auditors</title>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="icon" href="/img/icon.png" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/auditors-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> <style>
<script src="/dist/auditors-bundle.js"></script> body {
font-size: 100%;
<style> }
body { .tree-item {
font-size: 100%; margin: 2em;
} border-radius: 5px;
.tree-item { border: 1px solid gray;
margin: 2em; padding: 1em;
border-radius: 5px; }
border: 1px solid gray; .button-linky {
padding: 1em; background: none;
} color: black;
.button-linky { text-decoration: underline;
background: none; border: none;
color: black; }
text-decoration: underline; </style>
border: none; </head>
}
</style>
<body> <body>
<div id="container"></div> <div id="container"></div>

View File

@ -20,17 +20,13 @@
* @author Florian Dold * @author Florian Dold
*/ */
import { import {
AuditorRecord, AuditorRecord,
CurrencyRecord, CurrencyRecord,
ExchangeForCurrencyRecord, ExchangeForCurrencyRecord,
} from "../../types/dbTypes"; } from "../../types/dbTypes";
import { import { getCurrencies, updateCurrency } from "../wxApi";
getCurrencies,
updateCurrency,
} from "../wxApi";
import * as React from "react"; import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
@ -60,14 +56,22 @@ class CurrencyList extends React.Component<{}, CurrencyListState> {
} }
async confirmRemoveAuditor(c: CurrencyRecord, a: AuditorRecord) { async confirmRemoveAuditor(c: CurrencyRecord, a: AuditorRecord) {
if (window.confirm(`Do you really want to remove auditor ${a.baseUrl} for currency ${c.name}?`)) { if (
window.confirm(
`Do you really want to remove auditor ${a.baseUrl} for currency ${c.name}?`,
)
) {
c.auditors = c.auditors.filter((x) => x.auditorPub !== a.auditorPub); c.auditors = c.auditors.filter((x) => x.auditorPub !== a.auditorPub);
await updateCurrency(c); await updateCurrency(c);
} }
} }
async confirmRemoveExchange(c: CurrencyRecord, e: ExchangeForCurrencyRecord) { async confirmRemoveExchange(c: CurrencyRecord, e: ExchangeForCurrencyRecord) {
if (window.confirm(`Do you really want to remove exchange ${e.baseUrl} for currency ${c.name}?`)) { if (
window.confirm(
`Do you really want to remove exchange ${e.baseUrl} for currency ${c.name}?`,
)
) {
c.exchanges = c.exchanges.filter((x) => x.baseUrl !== e.baseUrl); c.exchanges = c.exchanges.filter((x) => x.baseUrl !== e.baseUrl);
await updateCurrency(c); await updateCurrency(c);
} }
@ -81,18 +85,21 @@ class CurrencyList extends React.Component<{}, CurrencyListState> {
<div> <div>
<p>Trusted Auditors:</p> <p>Trusted Auditors:</p>
<ul> <ul>
{c.auditors.map((a) => ( {c.auditors.map((a) => (
<li> <li>
{a.baseUrl}{" "} {a.baseUrl}{" "}
<button className="pure-button button-destructive" onClick={() => this.confirmRemoveAuditor(c, a)}> <button
Remove className="pure-button button-destructive"
</button> onClick={() => this.confirmRemoveAuditor(c, a)}
<ul> >
<li>valid until {new Date(a.expirationStamp).toString()}</li> Remove
<li>public key {a.auditorPub}</li> </button>
</ul> <ul>
</li> <li>valid until {new Date(a.expirationStamp).toString()}</li>
))} <li>public key {a.auditorPub}</li>
</ul>
</li>
))}
</ul> </ul>
</div> </div>
); );
@ -106,14 +113,17 @@ class CurrencyList extends React.Component<{}, CurrencyListState> {
<div> <div>
<p>Trusted Exchanges:</p> <p>Trusted Exchanges:</p>
<ul> <ul>
{c.exchanges.map((e) => ( {c.exchanges.map((e) => (
<li> <li>
{e.baseUrl}{" "} {e.baseUrl}{" "}
<button className="pure-button button-destructive" onClick={() => this.confirmRemoveExchange(c, e)}> <button
Remove className="pure-button button-destructive"
</button> onClick={() => this.confirmRemoveExchange(c, e)}
</li> >
))} Remove
</button>
</li>
))}
</ul> </ul>
</div> </div>
); );
@ -126,16 +136,16 @@ class CurrencyList extends React.Component<{}, CurrencyListState> {
} }
return ( return (
<div id="main"> <div id="main">
{currencies.map((c) => ( {currencies.map((c) => (
<div> <div>
<h1>Currency {c.name}</h1> <h1>Currency {c.name}</h1>
<p>Displayed with {c.fractionalDigits} fractional digits.</p> <p>Displayed with {c.fractionalDigits} fractional digits.</p>
<h2>Auditors</h2> <h2>Auditors</h2>
<div>{this.renderAuditors(c)}</div> <div>{this.renderAuditors(c)}</div>
<h2>Exchanges</h2> <h2>Exchanges</h2>
<div>{this.renderExchanges(c)}</div> <div>{this.renderExchanges(c)}</div>
</div> </div>
))} ))}
</div> </div>
); );
} }

View File

@ -1,17 +1,17 @@
<!doctype html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Taler Wallet: Benchmarks</title> <title>Taler Wallet: Benchmarks</title>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="icon" href="/img/icon.png"> <link rel="icon" href="/img/icon.png" />
<script src="/dist/page-common-bundle.js"></script> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/benchmark-bundle.js"></script> <script src="/dist/benchmark-bundle.js"></script>
</head> </head>
<body> <body>
<section id="main"> <section id="main">
<h1>Benchmarks</h1> <h1>Benchmarks</h1>
<div id="container"></div> <div id="container"></div>
</section> </section>
</body> </body>
</html> </html>

View File

@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* Benchmarks for the wallet. * Benchmarks for the wallet.
* *
@ -31,14 +30,12 @@ import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import { registerMountPage } from "../renderHtml"; import { registerMountPage } from "../renderHtml";
interface BenchmarkRunnerState { interface BenchmarkRunnerState {
repetitions: number; repetitions: number;
result?: BenchmarkResult; result?: BenchmarkResult;
running: boolean; running: boolean;
} }
function BenchmarkDisplay(props: BenchmarkRunnerState) { function BenchmarkDisplay(props: BenchmarkRunnerState) {
const result = props.result; const result = props.result;
if (!result) { if (!result) {
@ -50,24 +47,23 @@ function BenchmarkDisplay(props: BenchmarkRunnerState) {
} }
return ( return (
<> <>
<h2>Results for {result.repetitions} repetitions</h2> <h2>Results for {result.repetitions} repetitions</h2>
<table className="pure-table"> <table className="pure-table">
<thead> <thead>
<tr> <tr>
<th>{i18n.str`Operation`}</th> <th>{i18n.str`Operation`}</th>
<th>{i18n.str`time (ms/op)`}</th> <th>{i18n.str`time (ms/op)`}</th>
</tr> </tr>
{ {Object.keys(result.time)
Object.keys(result.time).sort().map( .sort()
k => .map((k) => (
<tr> <tr>
<td>{k}</td> <td>{k}</td>
<td>{result.time[k] / result.repetitions}</td> <td>{result.time[k] / result.repetitions}</td>
</tr> </tr>
) ))}
} </thead>
</thead> </table>
</table>
</> </>
); );
} }
@ -91,10 +87,13 @@ class BenchmarkRunner extends React.Component<any, BenchmarkRunnerState> {
return ( return (
<div> <div>
<label>Repetitions:</label> <label>Repetitions:</label>
<input type="number" <input
value={this.state.repetitions} type="number"
onChange={(evt) => this.setState({ repetitions: Number.parseInt(evt.target.value) })} /> value={this.state.repetitions}
{" "} onChange={(evt) =>
this.setState({ repetitions: Number.parseInt(evt.target.value) })
}
/>{" "}
<button onClick={() => this.run()}>Run</button> <button onClick={() => this.run()}>Run</button>
<BenchmarkDisplay {...this.state} /> <BenchmarkDisplay {...this.state} />
</div> </div>

View File

@ -1,79 +1,76 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Confirm Contract</title>
<head> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<title>Taler Wallet: Confirm Contract</title>
<link rel="stylesheet" type="text/css" href="../style/pure.css"> <link rel="icon" href="/img/icon.png" />
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/pay-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> <style>
<script src="/dist/pay-bundle.js"></script> button.accept {
background-color: #5757d2;
border: 1px solid black;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background: none !important;
border: none;
padding: 0 !important;
<style> font-family: arial, sans-serif;
button.accept { color: #069;
background-color: #5757D2; text-decoration: underline;
border: 1px solid black; cursor: pointer;
border-radius: 5px; }
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background:none!important;
border:none;
padding:0!important;
font-family:arial,sans-serif; input.url {
color:#069; width: 25em;
text-decoration:underline; }
cursor:pointer;
}
input.url { button.accept:disabled {
width: 25em; background-color: #dedbe8;
} border: 1px solid white;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: #2c2c2c;
}
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #ff8a8a;
}
button.accept:disabled { .okaybox {
background-color: #dedbe8; border: 1px solid;
border: 1px solid white; display: inline-block;
border-radius: 5px; margin: 1em;
margin: 1em 0; padding: 1em;
padding: 0.5em; font-weight: bold;
font-weight: bold; background: #00fa9a;
color: #2C2C2C; }
} </style>
</head>
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #FF8A8A;
}
.okaybox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #00FA9A;
}
</style>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<article id="container" class="fade"></article>
</section>
</body>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<article id="container" class="fade"></article>
</section>
</body>
</html> </html>

View File

@ -78,17 +78,15 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
let contractTerms: ContractTerms; let contractTerms: ContractTerms;
try { try {
contractTerms = codecForContractTerms().decode(JSON.parse(payStatus.contractTermsRaw)); contractTerms = codecForContractTerms().decode(
JSON.parse(payStatus.contractTermsRaw),
);
} catch (e) { } catch (e) {
// This should never happen, as the wallet is supposed to check the contract terms // This should never happen, as the wallet is supposed to check the contract terms
// before storing them. // before storing them.
console.error(e); console.error(e);
console.log("raw contract terms were", payStatus.contractTermsRaw); console.log("raw contract terms were", payStatus.contractTermsRaw);
return ( return <span>Invalid contract terms.</span>;
<span>
Invalid contract terms.
</span>
);
} }
if (!contractTerms) { if (!contractTerms) {
@ -149,7 +147,9 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
{insufficientBalance ? ( {insufficientBalance ? (
<div> <div>
<p style={{color: "red", fontWeight: "bold"}}>Unable to pay: Your balance is insufficient.</p> <p style={{ color: "red", fontWeight: "bold" }}>
Unable to pay: Your balance is insufficient.
</p>
</div> </div>
) : null} ) : null}
@ -168,7 +168,8 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
<ProgressButton <ProgressButton
loading={loading} loading={loading}
disabled={insufficientBalance} disabled={insufficientBalance}
onClick={() => doPayment()}> onClick={() => doPayment()}
>
{i18n.str`Confirm payment`} {i18n.str`Confirm payment`}
</ProgressButton> </ProgressButton>
</div> </div>

View File

@ -1,34 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Payback</title>
<head> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<meta charset="UTF-8">
<title>Taler Wallet: Payback</title>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="icon" href="/img/icon.png" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/payback-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> <style>
<script src="/dist/payback-bundle.js"></script> body {
font-size: 100%;
<style> }
body { .tree-item {
font-size: 100%; margin: 2em;
} border-radius: 5px;
.tree-item { border: 1px solid gray;
margin: 2em; padding: 1em;
border-radius: 5px; }
border: 1px solid gray; .button-linky {
padding: 1em; background: none;
} color: black;
.button-linky { text-decoration: underline;
background: none; border: none;
color: black; }
text-decoration: underline; </style>
border: none; </head>
}
</style>
<body> <body>
<div id="container"></div> <div id="container"></div>

View File

@ -1,4 +1,3 @@
/** /**
* @author Gabor X. Toth * @author Gabor X. Toth
* @author Marcello Stanisci * @author Marcello Stanisci
@ -6,182 +5,181 @@
*/ */
body { body {
min-height: 20em; min-height: 20em;
width: 30em; width: 30em;
margin: 0; margin: 0;
padding: 0; padding: 0;
max-height: 800px; max-height: 800px;
overflow: hidden; overflow: hidden;
background-color: #f8faf7; background-color: #f8faf7;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
.nav { .nav {
background-color: #033; background-color: #033;
padding: 0.5em 0; padding: 0.5em 0;
} }
.nav a { .nav a {
color: #f8faf7; color: #f8faf7;
padding: 0.7em 1.4em; padding: 0.7em 1.4em;
text-decoration: none; text-decoration: none;
} }
.nav a.active { .nav a.active {
background-color: #f8faf7; background-color: #f8faf7;
color: #000; color: #000;
font-weight: bold; font-weight: bold;
} }
.container { .container {
overflow-y: scroll; overflow-y: scroll;
max-height: 400px; max-height: 400px;
} }
.abbrev { .abbrev {
text-decoration-style: dotted; text-decoration-style: dotted;
} }
#content { #content {
padding: 1em; padding: 1em;
} }
#wallet-table .amount { #wallet-table .amount {
text-align: right; text-align: right;
} }
.hidden { .hidden {
display: none; display: none;
} }
#transactions-table th, #transactions-table th,
#transactions-table td { #transactions-table td {
padding: 0.2em 0.5em; padding: 0.2em 0.5em;
} }
#reserve-create table { #reserve-create table {
width: 100%; width: 100%;
} }
#reserve-create table td.label { #reserve-create table td.label {
width: 5em; width: 5em;
} }
#reserve-create table .input input[type="text"] { #reserve-create table .input input[type="text"] {
width: 100%; width: 100%;
} }
.historyItem { .historyItem {
min-width: 380px; min-width: 380px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
border-bottom: 1px solid #d9dbd8; border-bottom: 1px solid #d9dbd8;
padding: 0.5em; padding: 0.5em;
align-items: center; align-items: center;
} }
.historyItem .amount { .historyItem .amount {
font-size: 110%; font-size: 110%;
font-weight: bold; font-weight: bold;
text-align: right; text-align: right;
} }
.historyDate, .historyDate,
.historyTitle, .historyTitle,
.historyText, .historyText,
.historySmall { .historySmall {
margin: 0.3em; margin: 0.3em;
} }
.historyDate { .historyDate {
font-size: 90%; font-size: 90%;
color: slategray; color: slategray;
text-align: right; text-align: right;
} }
.historyLeft { .historyLeft {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
text-align: right; text-align: right;
} }
.historyContent { .historyContent {
flex: 1; flex: 1;
} }
.historyTitle { .historyTitle {
font-weight: 400; font-weight: 400;
} }
.historyText { .historyText {
font-size: 90%; font-size: 90%;
} }
.historySmall { .historySmall {
font-size: 70%; font-size: 70%;
text-transform: uppercase; text-transform: uppercase;
} }
.historyAmount { .historyAmount {
flex-grow: 1; flex-grow: 1;
} }
.historyAmount .primary { .historyAmount .primary {
font-size: 100%; font-size: 100%;
} }
.historyAmount .secondary { .historyAmount .secondary {
font-size: 80%; font-size: 80%;
} }
.historyAmount .positive { .historyAmount .positive {
color: #088; color: #088;
} }
.historyAmount .positive:before { .historyAmount .positive:before {
content: "+"; content: "+";
} }
.historyAmount .negative { .historyAmount .negative {
color: #800 color: #800;
} }
.historyAmount .negative:before { .historyAmount .negative:before {
color: #800; color: #800;
content: "-"; content: "-";
} }
.icon { .icon {
margin: 0 10px; margin: 0 10px;
text-align: center; text-align: center;
width: 35px; width: 35px;
font-size: 20px; font-size: 20px;
border-radius: 50%; border-radius: 50%;
background: #ccc; background: #ccc;
color: #fff; color: #fff;
padding-top: 4px; padding-top: 4px;
height: 30px; height: 30px;
} }
.option { .option {
text-transform: uppercase; text-transform: uppercase;
text-align: right; text-align: right;
padding: 0.4em; padding: 0.4em;
font-size: 0.9em; font-size: 0.9em;
} }
input[type=checkbox], input[type=radio] { input[type="checkbox"],
vertical-align: middle; input[type="radio"] {
position: relative; vertical-align: middle;
bottom: 1px; position: relative;
} bottom: 1px;
}
input[type=radio] { input[type="radio"] {
bottom: 2px; bottom: 2px;
} }
.balance { .balance {
text-align: center; text-align: center;
padding-top: 2em; padding-top: 2em;
} }

View File

@ -1,18 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="utf-8" />
<head> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="popup.css" />
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <script src="/dist/page-common-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="popup.css"> <script src="/dist/popup-bundle.js"></script>
</head>
<script src="/dist/page-common-bundle.js"></script>
<script src="/dist/popup-bundle.js"></script>
</head>
<body>
<div id="container" style="margin:0;padding:0"></div>
</body>
<body>
<div id="container" style="margin: 0; padding: 0;"></div>
</body>
</html> </html>

View File

@ -29,10 +29,7 @@ import * as i18n from "../i18n";
import { AmountJson } from "../../util/amounts"; import { AmountJson } from "../../util/amounts";
import * as Amounts from "../../util/amounts"; import * as Amounts from "../../util/amounts";
import { import { WalletBalance, WalletBalanceEntry } from "../../types/walletTypes";
WalletBalance,
WalletBalanceEntry,
} from "../../types/walletTypes";
import { import {
abbrev, abbrev,
@ -98,7 +95,7 @@ class Router extends React.Component<any, any> {
console.log("rendering route", route); console.log("rendering route", route);
let defaultChild: React.ReactChild | null = null; let defaultChild: React.ReactChild | null = null;
let foundChild: React.ReactChild | null = null; let foundChild: React.ReactChild | null = null;
React.Children.forEach(this.props.children, child => { React.Children.forEach(this.props.children, (child) => {
const childProps: any = (child as any).props; const childProps: any = (child as any).props;
if (!childProps) { if (!childProps) {
return; return;
@ -188,7 +185,7 @@ function bigAmount(amount: AmountJson): JSX.Element {
const v = amount.value + amount.fraction / Amounts.fractionalBase; const v = amount.value + amount.fraction / Amounts.fractionalBase;
return ( return (
<span> <span>
<span style={{ fontSize: "5em", display: 'block'}}>{v}</span>{" "} <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "}
<span>{amount.currency}</span> <span>{amount.currency}</span>
</span> </span>
); );
@ -281,7 +278,7 @@ class WalletBalanceView extends React.Component<any, any> {
); );
} }
const l = [incoming, payment].filter(x => x !== undefined); const l = [incoming, payment].filter((x) => x !== undefined);
if (l.length === 0) { if (l.length === 0) {
return <span />; return <span />;
} }
@ -300,7 +297,7 @@ class WalletBalanceView extends React.Component<any, any> {
const wallet = this.balance; const wallet = this.balance;
if (this.gotError) { if (this.gotError) {
return ( return (
<div className='balance'> <div className="balance">
<p>{i18n.str`Error: could not retrieve balance information.`}</p> <p>{i18n.str`Error: could not retrieve balance information.`}</p>
<p> <p>
Click <PageLink pageName="welcome.html">here</PageLink> for help and Click <PageLink pageName="welcome.html">here</PageLink> for help and
@ -313,7 +310,7 @@ class WalletBalanceView extends React.Component<any, any> {
return <span></span>; return <span></span>;
} }
console.log(wallet); console.log(wallet);
const listing = Object.keys(wallet.byCurrency).map(key => { const listing = Object.keys(wallet.byCurrency).map((key) => {
const entry: WalletBalanceEntry = wallet.byCurrency[key]; const entry: WalletBalanceEntry = wallet.byCurrency[key];
return ( return (
<p> <p>
@ -321,7 +318,11 @@ class WalletBalanceView extends React.Component<any, any> {
</p> </p>
); );
}); });
return listing.length > 0 ? <div className='balance'>{listing}</div> : <EmptyBalanceView />; return listing.length > 0 ? (
<div className="balance">{listing}</div>
) : (
<EmptyBalanceView />
);
} }
} }
@ -356,7 +357,7 @@ function HistoryItem({
invalid, invalid,
timestamp, timestamp,
icon, icon,
negative = false negative = false,
}: HistoryItemProps) { }: HistoryItemProps) {
function formatDate(timestamp: number | "never") { function formatDate(timestamp: number | "never") {
if (timestamp !== "never") { if (timestamp !== "never") {
@ -425,7 +426,7 @@ function HistoryItem({
function amountDiff( function amountDiff(
total: string | Amounts.AmountJson, total: string | Amounts.AmountJson,
partial: string | Amounts.AmountJson partial: string | Amounts.AmountJson,
): Amounts.AmountJson | string { ): Amounts.AmountJson | string {
let a = typeof total === "string" ? Amounts.parse(total) : total; let a = typeof total === "string" ? Amounts.parse(total) : total;
let b = typeof partial === "string" ? Amounts.parse(partial) : partial; let b = typeof partial === "string" ? Amounts.parse(partial) : partial;
@ -436,12 +437,11 @@ function amountDiff(
} }
} }
function parseSummary(summary: string) { function parseSummary(summary: string) {
let parsed = summary.split(/: (.+)/); let parsed = summary.split(/: (.+)/);
return { return {
merchant: parsed[0], merchant: parsed[0],
item: parsed[1] item: parsed[1],
}; };
} }
@ -454,7 +454,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
small={i18n.str`Refresh sessions has completed`} small={i18n.str`Refresh sessions has completed`}
fees={amountDiff( fees={amountDiff(
historyItem.amountRefreshedRaw, historyItem.amountRefreshedRaw,
historyItem.amountRefreshedEffective historyItem.amountRefreshedEffective,
)} )}
/> />
); );
@ -462,7 +462,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
case "order-refused": { case "order-refused": {
const { merchant, item } = parseSummary( const { merchant, item } = parseSummary(
historyItem.orderShortInfo.summary historyItem.orderShortInfo.summary,
); );
return ( return (
<HistoryItem <HistoryItem
@ -477,7 +477,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
case "order-redirected": { case "order-redirected": {
const { merchant, item } = parseSummary( const { merchant, item } = parseSummary(
historyItem.newOrderShortInfo.summary historyItem.newOrderShortInfo.summary,
); );
return ( return (
<HistoryItem <HistoryItem
@ -492,7 +492,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
case "payment-aborted": { case "payment-aborted": {
const { merchant, item } = parseSummary( const { merchant, item } = parseSummary(
historyItem.orderShortInfo.summary historyItem.orderShortInfo.summary,
); );
return ( return (
<HistoryItem <HistoryItem
@ -510,18 +510,15 @@ function formatHistoryItem(historyItem: HistoryEvent) {
case "payment-sent": { case "payment-sent": {
const url = historyItem.orderShortInfo.fulfillmentUrl; const url = historyItem.orderShortInfo.fulfillmentUrl;
const { merchant, item } = parseSummary( const { merchant, item } = parseSummary(
historyItem.orderShortInfo.summary historyItem.orderShortInfo.summary,
); );
const fees = amountDiff( const fees = amountDiff(
historyItem.amountPaidWithFees, historyItem.amountPaidWithFees,
historyItem.orderShortInfo.amount historyItem.orderShortInfo.amount,
); );
const fulfillmentLinkElem = ( const fulfillmentLinkElem = (
<Fragment> <Fragment>
<a <a href={url} onClick={openTab(url)}>
href={url}
onClick={openTab(url)}
>
{item ? abbrev(item, 30) : null} {item ? abbrev(item, 30) : null}
</a> </a>
</Fragment> </Fragment>
@ -542,14 +539,11 @@ function formatHistoryItem(historyItem: HistoryEvent) {
case "order-accepted": { case "order-accepted": {
const url = historyItem.orderShortInfo.fulfillmentUrl; const url = historyItem.orderShortInfo.fulfillmentUrl;
const { merchant, item } = parseSummary( const { merchant, item } = parseSummary(
historyItem.orderShortInfo.summary historyItem.orderShortInfo.summary,
); );
const fulfillmentLinkElem = ( const fulfillmentLinkElem = (
<Fragment> <Fragment>
<a <a href={url} onClick={openTab(url)}>
href={url}
onClick={openTab(url)}
>
{item ? abbrev(item, 40) : null} {item ? abbrev(item, 40) : null}
</a> </a>
</Fragment> </Fragment>
@ -573,7 +567,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
small={i18n.str`Reserve balance updated`} small={i18n.str`Reserve balance updated`}
fees={amountDiff( fees={amountDiff(
historyItem.amountExpected, historyItem.amountExpected,
historyItem.amountReserveBalance historyItem.amountReserveBalance,
)} )}
/> />
); );
@ -593,9 +587,9 @@ function formatHistoryItem(historyItem: HistoryEvent) {
fees={amountDiff( fees={amountDiff(
amountDiff( amountDiff(
historyItem.amountRefundedRaw, historyItem.amountRefundedRaw,
historyItem.amountRefundedInvalid historyItem.amountRefundedInvalid,
), ),
historyItem.amountRefundedEffective historyItem.amountRefundedEffective,
)} )}
/> />
); );
@ -604,7 +598,7 @@ function formatHistoryItem(historyItem: HistoryEvent) {
const exchange = new URL(historyItem.exchangeBaseUrl).host; const exchange = new URL(historyItem.exchangeBaseUrl).host;
const fees = amountDiff( const fees = amountDiff(
historyItem.amountWithdrawnRaw, historyItem.amountWithdrawnRaw,
historyItem.amountWithdrawnEffective historyItem.amountWithdrawnEffective,
); );
return ( return (
<HistoryItem <HistoryItem
@ -663,7 +657,7 @@ class WalletHistory extends React.Component<any, any> {
"refreshed", "refreshed",
"reserve-balance-updated", "reserve-balance-updated",
"exchange-updated", "exchange-updated",
"exchange-added" "exchange-added",
]; ];
componentWillMount() { componentWillMount() {
@ -678,7 +672,7 @@ class WalletHistory extends React.Component<any, any> {
} }
update() { update() {
chrome.runtime.sendMessage({ type: "get-history" }, resp => { chrome.runtime.sendMessage({ type: "get-history" }, (resp) => {
if (this.unmounted) { if (this.unmounted) {
return; return;
} }
@ -709,15 +703,13 @@ class WalletHistory extends React.Component<any, any> {
} }
const listing: any[] = []; const listing: any[] = [];
const messages = history const messages = history.reverse().filter((hEvent) => {
.reverse() if (!this.state.filter) return true;
.filter(hEvent => { return this.hidenTypes.indexOf(hEvent.type) === -1;
if (!this.state.filter) return true; });
return this.hidenTypes.indexOf(hEvent.type) === -1;
});
for (const record of messages) { for (const record of messages) {
const item = (<HistoryComponent key={record.eventId} record={record} />); const item = <HistoryComponent key={record.eventId} record={record} />;
listing.push(item); listing.push(item);
} }
@ -821,5 +813,5 @@ function WalletPopup() {
registerMountPage(() => { registerMountPage(() => {
chrome.runtime.connect({ name: "popup" }); chrome.runtime.connect({ name: "popup" });
return <WalletPopup /> return <WalletPopup />;
}); });

View File

@ -1,14 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="utf-8" />
<head> <script src="/src/webex/pages/redirect.js"></script>
<meta charset="utf-8"> </head>
<script src="/src/webex/pages/redirect.js"></script>
</head>
<body>
Redirecting to extension page ...
</body>
<body>
Redirecting to extension page ...
</body>
</html> </html>

View File

@ -1,16 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Refund Status</title>
<head> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<meta charset="UTF-8">
<title>Taler Wallet: Refund Status</title>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="icon" href="/img/icon.png" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/refund-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> </head>
<script src="/dist/refund-bundle.js"></script>
<body> <body>
<section id="main"> <section id="main">

View File

@ -65,11 +65,10 @@ function RefundStatusView(props: { talerRefundUri: string }) {
<h2>Refund Status</h2> <h2>Refund Status</h2>
<p> <p>
The product <em>{purchaseDetails.contractTerms.summary!}</em> has The product <em>{purchaseDetails.contractTerms.summary!}</em> has
received a total refund of <AmountView amount={purchaseDetails.totalRefundAmount} />. received a total refund of{" "}
</p> <AmountView amount={purchaseDetails.totalRefundAmount} />.
<p>
Note that additional fees from the exchange may apply.
</p> </p>
<p>Note that additional fees from the exchange may apply.</p>
</> </>
); );
} }

View File

@ -1,30 +1,27 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Select Taler Provider</title>
<head> <link rel="icon" href="/img/icon.png" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<title>Taler Wallet: Select Taler Provider</title> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <script src="/dist/reset-required-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/pure.css">
<script src="/dist/page-common-bundle.js"></script> <style>
<script src="/dist/reset-required-bundle.js"></script> body {
font-size: 100%;
<style> overflow-y: scroll;
body { }
font-size: 100%; </style>
overflow-y: scroll; </head>
}
</style>
</head>
<body>
<section id="main">
<div id="container"></div>
</section>
</body>
<body>
<section id="main">
<div id="container"></div>
</section>
</body>
</html> </html>

View File

@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* Page to inform the user when a database reset is required. * Page to inform the user when a database reset is required.
* *
@ -38,16 +37,15 @@ class State {
resetRequired: boolean; resetRequired: boolean;
} }
class ResetNotification extends React.Component<any, State> { class ResetNotification extends React.Component<any, State> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = {checked: false, resetRequired: true}; this.state = { checked: false, resetRequired: true };
setInterval(() => this.update(), 500); setInterval(() => this.update(), 500);
} }
async update() { async update() {
const res = await wxApi.checkUpgrade(); const res = await wxApi.checkUpgrade();
this.setState({resetRequired: res.dbResetRequired}); this.setState({ resetRequired: res.dbResetRequired });
} }
render() { render() {
if (this.state.resetRequired) { if (this.state.resetRequired) {
@ -55,32 +53,42 @@ class ResetNotification extends React.Component<any, State> {
<div> <div>
<h1>Manual Reset Reqired</h1> <h1>Manual Reset Reqired</h1>
<p> <p>
The wallet's database in your browser is incompatible with the {" "} The wallet's database in your browser is incompatible with the{" "}
currently installed wallet. Please reset manually. currently installed wallet. Please reset manually.
</p> </p>
<p>Once the database format has stabilized, we will provide automatic upgrades.</p> <p>
<input id="check" Once the database format has stabilized, we will provide automatic
type="checkbox" upgrades.
checked={this.state.checked} </p>
onChange={(e) => this.setState({checked: e.target.checked})} />{" "} <input
id="check"
type="checkbox"
checked={this.state.checked}
onChange={(e) => this.setState({ checked: e.target.checked })}
/>{" "}
<label htmlFor="check"> <label htmlFor="check">
I understand that I will lose all my data I understand that I will lose all my data
</label> </label>
<br /> <br />
<button className="pure-button" disabled={!this.state.checked} onClick={() => wxApi.resetDb()}>Reset</button> <button
className="pure-button"
disabled={!this.state.checked}
onClick={() => wxApi.resetDb()}
>
Reset
</button>
</div> </div>
); );
} }
return ( return (
<div> <div>
<h1>Everything is fine!</h1> <h1>Everything is fine!</h1>A reset is not required anymore, you can
A reset is not required anymore, you can close this page. close this page.
</div> </div>
); );
} }
} }
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
ReactDOM.render(<ResetNotification />, document.getElementById( "container")!); ReactDOM.render(<ResetNotification />, document.getElementById("container")!);
}); });

View File

@ -1,17 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Return Coins to Bank Account</title>
<head> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<title>Taler Wallet: Return Coins to Bank Account</title>
<link rel="stylesheet" type="text/css" href="../style/pure.css"> <link rel="icon" href="/img/icon.png" />
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/return-coins-bundle.js"></script>
<script src="/dist/page-common-bundle.js"></script> </head>
<script src="/dist/return-coins-bundle.js"></script>
<body> <body>
<div id="container"></div> <div id="container"></div>

View File

@ -20,7 +20,6 @@
* @author Florian Dold * @author Florian Dold
*/ */
/** /**
* Imports. * Imports.
*/ */
@ -28,20 +27,13 @@
import { AmountJson } from "../../util/amounts"; import { AmountJson } from "../../util/amounts";
import * as Amounts from "../../util/amounts"; import * as Amounts from "../../util/amounts";
import { import { SenderWireInfos, WalletBalance } from "../../types/walletTypes";
SenderWireInfos,
WalletBalance,
} from "../../types/walletTypes";
import * as i18n from "../i18n"; import * as i18n from "../i18n";
import * as wire from "../../util/wire"; import * as wire from "../../util/wire";
import { import { getBalance, getSenderWireInfos, returnCoins } from "../wxApi";
getBalance,
getSenderWireInfos,
returnCoins,
} from "../wxApi";
import { renderAmount } from "../renderHtml"; import { renderAmount } from "../renderHtml";
@ -60,17 +52,27 @@ interface ReturnSelectionItemState {
currency: string; currency: string;
} }
class ReturnSelectionItem extends React.Component<ReturnSelectionItemProps, ReturnSelectionItemState> { class ReturnSelectionItem extends React.Component<
ReturnSelectionItemProps,
ReturnSelectionItemState
> {
constructor(props: ReturnSelectionItemProps) { constructor(props: ReturnSelectionItemProps) {
super(props); super(props);
const exchange = this.props.exchangeUrl; const exchange = this.props.exchangeUrl;
const wireTypes = this.props.senderWireInfos.exchangeWireTypes; const wireTypes = this.props.senderWireInfos.exchangeWireTypes;
const supportedWires = this.props.senderWireInfos.senderWires.filter((x) => { const supportedWires = this.props.senderWireInfos.senderWires.filter(
return wireTypes[exchange] && wireTypes[exchange].indexOf((x as any).type) >= 0; (x) => {
}); return (
wireTypes[exchange] &&
wireTypes[exchange].indexOf((x as any).type) >= 0
);
},
);
this.state = { this.state = {
currency: props.balance.byExchange[props.exchangeUrl].available.currency, currency: props.balance.byExchange[props.exchangeUrl].available.currency,
selectedValue: Amounts.toString(props.balance.byExchange[props.exchangeUrl].available), selectedValue: Amounts.toString(
props.balance.byExchange[props.exchangeUrl].available,
),
selectedWire: "", selectedWire: "",
supportedWires, supportedWires,
}; };
@ -83,28 +85,46 @@ class ReturnSelectionItem extends React.Component<ReturnSelectionItemProps, Retu
<div key={exchange}> <div key={exchange}>
<h2>Exchange {exchange}</h2> <h2>Exchange {exchange}</h2>
<p>Available amount: {renderAmount(byExchange[exchange].available)}</p> <p>Available amount: {renderAmount(byExchange[exchange].available)}</p>
<p>Supported wire methods: {wireTypes[exchange].length ? wireTypes[exchange].join(", ") : "none"}</p> <p>
<p>Wire {""} Supported wire methods:{" "}
<input {wireTypes[exchange].length ? wireTypes[exchange].join(", ") : "none"}
type="text"
size={this.state.selectedValue.length || 1}
value={this.state.selectedValue}
onChange={(evt) => this.setState({selectedValue: evt.target.value})}
style={{textAlign: "center"}}
/> {this.props.balance.byExchange[exchange].available.currency} {""}
to account {""}
<select value={this.state.selectedWire} onChange={(evt) => this.setState({selectedWire: evt.target.value})}>
<option style={{display: "none"}}>Select account</option>
{this.state.supportedWires.map((w, n) =>
<option value={n.toString()} key={JSON.stringify(w)}>{n + 1}: {wire.summarizeWire(w)}</option>,
)}
</select>.
</p> </p>
{this.state.selectedWire <p>
? <button className="pure-button button-success" onClick={() => this.select()}> Wire {""}
{i18n.str`Wire to bank account`} <input
</button> type="text"
: null} size={this.state.selectedValue.length || 1}
value={this.state.selectedValue}
onChange={(evt) =>
this.setState({ selectedValue: evt.target.value })
}
style={{ textAlign: "center" }}
/>{" "}
{this.props.balance.byExchange[exchange].available.currency} {""}
to account {""}
<select
value={this.state.selectedWire}
onChange={(evt) =>
this.setState({ selectedWire: evt.target.value })
}
>
<option style={{ display: "none" }}>Select account</option>
{this.state.supportedWires.map((w, n) => (
<option value={n.toString()} key={JSON.stringify(w)}>
{n + 1}: {wire.summarizeWire(w)}
</option>
))}
</select>
.
</p>
{this.state.selectedWire ? (
<button
className="pure-button button-success"
onClick={() => this.select()}
>
{i18n.str`Wire to bank account`}
</button>
) : null}
</div> </div>
); );
} }
@ -133,16 +153,23 @@ interface ReturnSelectionListProps {
selectDetail(d: SelectedDetail): void; selectDetail(d: SelectedDetail): void;
} }
class ReturnSelectionList extends React.Component<ReturnSelectionListProps, {}> { class ReturnSelectionList extends React.Component<
ReturnSelectionListProps,
{}
> {
render(): JSX.Element { render(): JSX.Element {
const byExchange = this.props.balance.byExchange; const byExchange = this.props.balance.byExchange;
const exchanges = Object.keys(byExchange); const exchanges = Object.keys(byExchange);
if (!exchanges.length) { if (!exchanges.length) {
return <p className="errorbox">Currently no funds available to transfer.</p>; return (
<p className="errorbox">Currently no funds available to transfer.</p>
);
} }
return ( return (
<div> <div>
{exchanges.map((e) => <ReturnSelectionItem key={e} exchangeUrl={e} {...this.props} />)} {exchanges.map((e) => (
<ReturnSelectionItem key={e} exchangeUrl={e} {...this.props} />
))}
</div> </div>
); );
} }
@ -154,7 +181,6 @@ interface SelectedDetail {
exchange: string; exchange: string;
} }
interface ReturnConfirmationProps { interface ReturnConfirmationProps {
detail: SelectedDetail; detail: SelectedDetail;
cancel(): void; cancel(): void;
@ -165,11 +191,19 @@ class ReturnConfirmation extends React.Component<ReturnConfirmationProps, {}> {
render() { render() {
return ( return (
<div> <div>
<p>Please confirm if you want to transmit <strong>{renderAmount(this.props.detail.amount)}</strong> at {""} <p>
{this.props.detail.exchange} to account {""} Please confirm if you want to transmit{" "}
<strong style={{whiteSpace: "nowrap"}}>{wire.summarizeWire(this.props.detail.senderWire)}</strong>. <strong>{renderAmount(this.props.detail.amount)}</strong> at {""}
{this.props.detail.exchange} to account {""}
<strong style={{ whiteSpace: "nowrap" }}>
{wire.summarizeWire(this.props.detail.senderWire)}
</strong>
.
</p> </p>
<button className="pure-button button-success" onClick={() => this.props.confirm()}> <button
className="pure-button button-success"
onClick={() => this.props.confirm()}
>
{i18n.str`Confirm`} {i18n.str`Confirm`}
</button> </button>
<button className="pure-button" onClick={() => this.props.cancel()}> <button className="pure-button" onClick={() => this.props.cancel()}>
@ -213,7 +247,7 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> {
} }
selectDetail(d: SelectedDetail) { selectDetail(d: SelectedDetail) {
this.setState({selectedReturn: d}); this.setState({ selectedReturn: d });
} }
async confirm() { async confirm() {
@ -223,11 +257,17 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> {
} }
await returnCoins(selectedReturn); await returnCoins(selectedReturn);
await this.update(); await this.update();
this.setState({selectedReturn: undefined, lastConfirmedDetail: selectedReturn}); this.setState({
selectedReturn: undefined,
lastConfirmedDetail: selectedReturn,
});
} }
async cancel() { async cancel() {
this.setState({selectedReturn: undefined, lastConfirmedDetail: undefined}); this.setState({
selectedReturn: undefined,
lastConfirmedDetail: undefined,
});
} }
render() { render() {
@ -248,25 +288,28 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> {
); );
} }
return ( return (
<div id="main"> <div id="main">
<h1>Wire electronic cash back to own bank account</h1> <h1>Wire electronic cash back to own bank account</h1>
<p>You can send coins back into your own bank account. Note that <p>
you're acting as a merchant when doing this, and thus the same fees apply.</p> You can send coins back into your own bank account. Note that you're
{this.state.lastConfirmedDetail acting as a merchant when doing this, and thus the same fees apply.
? <p className="okaybox"> </p>
Transfer of {renderAmount(this.state.lastConfirmedDetail.amount)} successfully initiated. {this.state.lastConfirmedDetail ? (
</p> <p className="okaybox">
: null} Transfer of {renderAmount(this.state.lastConfirmedDetail.amount)}{" "}
<ReturnSelectionList successfully initiated.
selectDetail={(d) => this.selectDetail(d)} </p>
balance={balance} ) : null}
senderWireInfos={senderWireInfos} /> <ReturnSelectionList
</div> selectDetail={(d) => this.selectDetail(d)}
balance={balance}
senderWireInfos={senderWireInfos}
/>
</div>
); );
} }
} }
function main() { function main() {
ReactDOM.render(<ReturnCoins />, document.getElementById("container")!); ReactDOM.render(<ReturnCoins />, document.getElementById("container")!);
} }

View File

@ -1,16 +1,16 @@
<!doctype html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Taler Wallet: Reserve Created</title> <title>Taler Wallet: Reserve Created</title>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="icon" href="/img/icon.png"> <link rel="icon" href="/img/icon.png" />
<script src="/dist/page-common-bundle.js"></script> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/show-db-bundle.js"></script> <script src="/dist/show-db-bundle.js"></script>
</head> </head>
<body> <body>
<h1>DB Dump</h1> <h1>DB Dump</h1>
<input type="file" id="fileInput" style="display:none"> <input type="file" id="fileInput" style="display: none;" />
<button id="import">Import Dump</button> <button id="import">Import Dump</button>
<button id="download">Download Dump</button> <button id="download">Download Dump</button>
<pre id="dump"></pre> <pre id="dump"></pre>

View File

@ -14,15 +14,19 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* Wallet database dump for debugging. * Wallet database dump for debugging.
* *
* @author Florian Dold * @author Florian Dold
*/ */
function replacer(match: string, pIndent: string, pKey: string, pVal: string, function replacer(
pEnd: string) { match: string,
pIndent: string,
pKey: string,
pVal: string,
pEnd: string,
) {
const key = "<span class=json-key>"; const key = "<span class=json-key>";
const val = "<span class=json-value>"; const val = "<span class=json-value>";
const str = "<span class=json-string>"; const str = "<span class=json-string>";
@ -36,18 +40,18 @@ function replacer(match: string, pIndent: string, pKey: string, pVal: string,
return r + (pEnd || ""); return r + (pEnd || "");
} }
function prettyPrint(obj: any) { function prettyPrint(obj: any) {
const jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg; const jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/gm;
return JSON.stringify(obj, null as any, 3) return JSON.stringify(obj, null as any, 3)
.replace(/&/g, "&amp;").replace(/\\"/g, "&quot;") .replace(/&/g, "&amp;")
.replace(/</g, "&lt;").replace(/>/g, "&gt;") .replace(/\\"/g, "&quot;")
.replace(jsonLine, replacer); .replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(jsonLine, replacer);
} }
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
chrome.runtime.sendMessage({type: "dump-db"}, (resp) => { chrome.runtime.sendMessage({ type: "dump-db" }, (resp) => {
const el = document.getElementById("dump"); const el = document.getElementById("dump");
if (!el) { if (!el) {
throw Error(); throw Error();
@ -57,16 +61,18 @@ document.addEventListener("DOMContentLoaded", () => {
document.getElementById("download")!.addEventListener("click", (evt) => { document.getElementById("download")!.addEventListener("click", (evt) => {
console.log("creating download"); console.log("creating download");
const element = document.createElement("a"); const element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(JSON.stringify(resp))); element.setAttribute(
"href",
"data:text/plain;charset=utf-8," +
encodeURIComponent(JSON.stringify(resp)),
);
element.setAttribute("download", "wallet-dump.txt"); element.setAttribute("download", "wallet-dump.txt");
element.style.display = "none"; element.style.display = "none";
document.body.appendChild(element); document.body.appendChild(element);
element.click(); element.click();
}); });
}); });
const fileInput = document.getElementById("fileInput")! as HTMLInputElement; const fileInput = document.getElementById("fileInput")! as HTMLInputElement;
fileInput.onchange = (evt) => { fileInput.onchange = (evt) => {
if (!fileInput.files || fileInput.files.length !== 1) { if (!fileInput.files || fileInput.files.length !== 1) {
@ -79,9 +85,12 @@ document.addEventListener("DOMContentLoaded", () => {
console.log("got file"); console.log("got file");
const dump = JSON.parse(e.target.result); const dump = JSON.parse(e.target.result);
console.log("parsed contents", dump); console.log("parsed contents", dump);
chrome.runtime.sendMessage({ type: "import-db", detail: { dump } }, (resp) => { chrome.runtime.sendMessage(
alert("loaded"); { type: "import-db", detail: { dump } },
}); (resp) => {
alert("loaded");
},
);
}; };
console.log("reading file", file); console.log("reading file", file);
fr.readAsText(file); fr.readAsText(file);

View File

@ -1,24 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Received Tip</title>
<head> <link rel="icon" href="/img/icon.png" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<title>Taler Wallet: Received Tip</title> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/pure.css"> <script src="/dist/tip-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> </head>
<script src="/dist/page-common-bundle.js"></script>
<script src="/dist/tip-bundle.js"></script>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<div id="container"></div>
</section>
</body>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<div id="container"></div>
</section>
</body>
</html> </html>

View File

@ -28,13 +28,16 @@ import * as i18n from "../i18n";
import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi"; import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi";
import { WithdrawDetailView, renderAmount, ProgressButton } from "../renderHtml"; import {
WithdrawDetailView,
renderAmount,
ProgressButton,
} from "../renderHtml";
import * as Amounts from "../../util/amounts"; import * as Amounts from "../../util/amounts";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { TipStatus } from "../../types/walletTypes"; import { TipStatus } from "../../types/walletTypes";
function TipDisplay(props: { talerTipUri: string }) { function TipDisplay(props: { talerTipUri: string }) {
const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined); const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined);
const [discarded, setDiscarded] = useState(false); const [discarded, setDiscarded] = useState(false);
@ -88,8 +91,7 @@ function TipDisplay(props: { talerTipUri: string }) {
<form className="pure-form"> <form className="pure-form">
<ProgressButton loading={loading} onClick={() => accept()}> <ProgressButton loading={loading} onClick={() => accept()}>
Accept Tip Accept Tip
</ProgressButton> </ProgressButton>{" "}
{" "}
<button className="pure-button" type="button" onClick={() => discard()}> <button className="pure-button" type="button" onClick={() => discard()}>
Discard tip Discard tip
</button> </button>

View File

@ -1,24 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Withdraw</title>
<head> <link rel="icon" href="/img/icon.png" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<title>Taler Wallet: Withdraw</title> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/pure.css"> <script src="/dist/welcome-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> </head>
<script src="/dist/page-common-bundle.js"></script>
<script src="/dist/welcome-bundle.js"></script>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet Installed!</h1>
<div id="container">Loading...</div>
</section>
</body>
<body>
<section id="main">
<h1>GNU Taler Wallet Installed!</h1>
<div id="container">Loading...</div>
</section>
</body>
</html> </html>

View File

@ -68,7 +68,7 @@ function Diagnostics() {
> >
<p>Problems detected:</p> <p>Problems detected:</p>
<ol> <ol>
{diagnostics.errors.map(errMsg => ( {diagnostics.errors.map((errMsg) => (
<li>{errMsg}</li> <li>{errMsg}</li>
))} ))}
</ol> </ol>

View File

@ -1,24 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Withdraw</title>
<head> <link rel="icon" href="/img/icon.png" />
<meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../style/pure.css" />
<title>Taler Wallet: Withdraw</title> <link rel="stylesheet" type="text/css" href="../style/wallet.css" />
<link rel="icon" href="/img/icon.png"> <script src="/dist/page-common-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/pure.css"> <script src="/dist/withdraw-bundle.js"></script>
<link rel="stylesheet" type="text/css" href="../style/wallet.css"> </head>
<script src="/dist/page-common-bundle.js"></script> <body>
<script src="/dist/withdraw-bundle.js"></script> <section id="main">
<h1>GNU Taler Wallet</h1>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<div class="fade" id="exchange-selection"></div> <div class="fade" id="exchange-selection"></div>
</section> </section>
</body> </body>
</html> </html>

View File

@ -127,7 +127,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) {
<p> <p>
<input <input
type="text" type="text"
onChange={e => setCustomUrl(e.target.value)} onChange={(e) => setCustomUrl(e.target.value)}
value={customUrl} value={customUrl}
/> />
</p> </p>

View File

@ -143,7 +143,7 @@ function AuditorDetailsView(props: {
} }
return ( return (
<div> <div>
{(rci.exchangeInfo.details?.auditors ?? []).map(a => ( {(rci.exchangeInfo.details?.auditors ?? []).map((a) => (
<div> <div>
<h3>Auditor {a.auditor_url}</h3> <h3>Auditor {a.auditor_url}</h3>
<p> <p>
@ -214,7 +214,7 @@ function FeeDetailsView(props: {
</tr> </tr>
</thead>, </thead>,
<tbody> <tbody>
{rci!.wireFees.feesForType[s].map(f => ( {rci!.wireFees.feesForType[s].map((f) => (
<tr> <tr>
<td>{stringifyTimestamp(f.endStamp)}</td> <td>{stringifyTimestamp(f.endStamp)}</td>
<td>{renderAmount(f.wireFee)}</td> <td>{renderAmount(f.wireFee)}</td>
@ -232,7 +232,10 @@ function FeeDetailsView(props: {
<div> <div>
<h3>Overview</h3> <h3>Overview</h3>
<p> <p>
Public key: <ExpanderText text={rci.exchangeInfo.details?.masterPublicKey ?? "??"} /> Public key:{" "}
<ExpanderText
text={rci.exchangeInfo.details?.masterPublicKey ?? "??"}
/>
</p> </p>
<p> <p>
{i18n.str`Withdrawal fees:`} {withdrawFee} {i18n.str`Withdrawal fees:`} {withdrawFee}
@ -240,8 +243,9 @@ function FeeDetailsView(props: {
<p> <p>
{i18n.str`Rounding loss:`} {overhead} {i18n.str`Rounding loss:`} {overhead}
</p> </p>
<p>{i18n.str`Earliest expiration (for deposit): ${ <p>{i18n.str`Earliest expiration (for deposit): ${stringifyTimestamp(
stringifyTimestamp(rci.earliestDepositExpiration)}`}</p> rci.earliestDepositExpiration,
)}`}</p>
<h3>Coin Fees</h3> <h3>Coin Fees</h3>
<div style={{ overflow: "auto" }}> <div style={{ overflow: "auto" }}>
<table className="pure-table"> <table className="pure-table">

File diff suppressed because it is too large Load Diff

View File

@ -4,75 +4,77 @@ body {
} }
#main { #main {
border: solid 1px black; border: solid 1px black;
border-radius: 10px; border-radius: 10px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
margin-top: 2em; margin-top: 2em;
max-width: 50%; max-width: 50%;
padding: 2em; padding: 2em;
} }
header { header {
width: 100%; width: 100%;
height: 100px; height: 100px;
margin: 0; margin: 0;
padding: 0; padding: 0;
border-bottom: 1px solid black; border-bottom: 1px solid black;
} }
header h1 { header h1 {
font-size: 200%; font-size: 200%;
margin: 0; margin: 0;
padding: 0 0 0 120px; padding: 0 0 0 120px;
position: relative; position: relative;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
} }
header #logo { header #logo {
float: left; float: left;
width: 100px; width: 100px;
height: 100px; height: 100px;
padding: 0; padding: 0;
margin: 0; margin: 0;
text-align: center; text-align: center;
border-right: 1px solid black; border-right: 1px solid black;
background-image: url(/img/logo.png); background-image: url(/img/logo.png);
background-size: 100px; background-size: 100px;
} }
aside { aside {
width: 100px; width: 100px;
float: left; float: left;
} }
section#main { section#main {
margin: auto; margin: auto;
padding: 20px; padding: 20px;
border-left: 1px solid black; border-left: 1px solid black;
height: 100%; height: 100%;
max-width: 50%; max-width: 50%;
} }
section#main h1:first-child { section#main h1:first-child {
margin-top: 0; margin-top: 0;
} }
h1 { h1 {
font-size: 160%; font-size: 160%;
} }
h2 { h2 {
font-size: 140%; font-size: 140%;
} }
h3 { h3 {
font-size: 120%; font-size: 120%;
} }
h4, h5, h6 { h4,
font-size: 100%; h5,
h6 {
font-size: 100%;
} }
.form-row { .form-row {
@ -92,63 +94,74 @@ input.url {
} }
.json-key { .json-key {
color: brown; color: brown;
} }
.json-value { .json-value {
color: navy; color: navy;
} }
.json-string { .json-string {
color: olive; color: olive;
} }
button { button {
font-size: 120%; font-size: 120%;
padding: 0.5em; padding: 0.5em;
} }
button.confirm-pay { button.confirm-pay {
float: right; float: right;
} }
/* We use fading to hide slower DOM updates */ /* We use fading to hide slower DOM updates */
.fade { .fade {
-webkit-animation: fade 0.7s; -webkit-animation: fade 0.7s;
animation: fade 0.7s; animation: fade 0.7s;
opacity: 1; opacity: 1;
} }
@-webkit-keyframes fade { @-webkit-keyframes fade {
from {opacity: 0} from {
to {opacity: 1} opacity: 0;
}
to {
opacity: 1;
}
} }
@keyframes fade { @keyframes fade {
from {opacity: 0} from {
to {opacity: 1} opacity: 0;
} }
to {
button.linky { opacity: 1;
background:none!important; }
border:none;
padding:0!important;
font-family:arial,sans-serif;
color:#069;
text-decoration:underline;
cursor:pointer;
} }
.blacklink a:link, .blacklink a:visited, .blacklink a:hover, .blacklink a:active { button.linky {
background: none !important;
border: none;
padding: 0 !important;
font-family: arial, sans-serif;
color: #069;
text-decoration: underline;
cursor: pointer;
}
.blacklink a:link,
.blacklink a:visited,
.blacklink a:hover,
.blacklink a:active {
color: #000; color: #000;
} }
table,
table, th, td { th,
border: 1px solid black; td {
border: 1px solid black;
} }
button.accept { button.accept {
background-color: #5757D2; background-color: #5757d2;
border: 1px solid black; border: 1px solid black;
border-radius: 5px; border-radius: 5px;
margin: 1em 0; margin: 1em 0;
@ -157,17 +170,16 @@ button.accept {
color: white; color: white;
} }
button.linky { button.linky {
background:none!important; background: none !important;
border:none; border: none;
padding:0!important; padding: 0 !important;
font-family:arial,sans-serif; font-family: arial, sans-serif;
color:#069; color: #069;
text-decoration:underline; text-decoration: underline;
cursor:pointer; cursor: pointer;
} }
button.accept:disabled { button.accept:disabled {
background-color: #dedbe8; background-color: #dedbe8;
border: 1px solid white; border: 1px solid white;
@ -175,7 +187,7 @@ button.accept:disabled {
margin: 1em 0; margin: 1em 0;
padding: 0.5em; padding: 0.5em;
font-weight: bold; font-weight: bold;
color: #2C2C2C; color: #2c2c2c;
} }
input.url { input.url {
@ -202,39 +214,38 @@ span.spacer {
.button-destructive, .button-destructive,
.button-warning, .button-warning,
.button-secondary { .button-secondary {
color: white; color: white;
border-radius: 4px; border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
} }
.button-success { .button-success {
background: rgb(28, 184, 65); background: rgb(28, 184, 65);
} }
.button-destructive { .button-destructive {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
} }
.button-warning { .button-warning {
background: rgb(223, 117, 20); background: rgb(223, 117, 20);
} }
.button-secondary { .button-secondary {
background: rgb(66, 184, 221); background: rgb(66, 184, 221);
} }
a.actionLink { a.actionLink {
color: black; color: black;
} }
.errorbox { .errorbox {
border: 1px solid; border: 1px solid;
display: inline-block; display: inline-block;
margin: 1em; margin: 1em;
padding: 1em; padding: 1em;
font-weight: bold; font-weight: bold;
background: #FF8A8A; background: #ff8a8a;
} }
.okaybox { .okaybox {
@ -243,18 +254,17 @@ a.actionLink {
margin: 1em; margin: 1em;
padding: 1em; padding: 1em;
font-weight: bold; font-weight: bold;
background: #00FA9A; background: #00fa9a;
} }
a.opener {
a.opener {
color: black; color: black;
} }
.opener-open::before { .opener-open::before {
content: "\25bc" content: "\25bc";
} }
.opener-collapsed::before { .opener-collapsed::before {
content: "\25b6 " content: "\25b6 ";
} }
.svg-icon { .svg-icon {
@ -265,8 +275,8 @@ a.opener {
width: 1em; width: 1em;
} }
.svg-icon svg { .svg-icon svg {
height:1em; height: 1em;
width:1em; width: 1em;
} }
object.svg-icon.svg-baseline { object.svg-icon.svg-baseline {
transform: translate(0, 0.125em); transform: translate(0, 0.125em);

View File

@ -79,7 +79,7 @@ async function callBackend<T extends MessageType>(
detail: MessageMap[T]["request"], detail: MessageMap[T]["request"],
): Promise<MessageMap[T]["response"]> { ): Promise<MessageMap[T]["response"]> {
return new Promise<MessageMap[T]["response"]>((resolve, reject) => { return new Promise<MessageMap[T]["response"]>((resolve, reject) => {
chrome.runtime.sendMessage({ type, detail }, resp => { chrome.runtime.sendMessage({ type, detail }, (resp) => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.log("Error calling backend"); console.log("Error calling backend");
reject( reject(

View File

@ -406,7 +406,7 @@ async function reinitWallet() {
http, http,
new BrowserCryptoWorkerFactory(), new BrowserCryptoWorkerFactory(),
); );
wallet.runRetryLoop().catch(e => { wallet.runRetryLoop().catch((e) => {
console.log("error during wallet retry loop", e); console.log("error during wallet retry loop", e);
}); });
// Useful for debugging in the background page. // Useful for debugging in the background page.
@ -443,7 +443,7 @@ function injectScript(
try { try {
// This needs to be outside of main, as Firefox won't fire the event if // This needs to be outside of main, as Firefox won't fire the event if
// the listener isn't created synchronously on loading the backend. // the listener isn't created synchronously on loading the backend.
chrome.runtime.onInstalled.addListener(details => { chrome.runtime.onInstalled.addListener((details) => {
console.log("onInstalled with reason", details.reason); console.log("onInstalled with reason", details.reason);
if (details.reason === "install") { if (details.reason === "install") {
const url = chrome.extension.getURL("/src/webex/pages/welcome.html"); const url = chrome.extension.getURL("/src/webex/pages/welcome.html");
@ -462,12 +462,12 @@ try {
export async function wxMain() { export async function wxMain() {
// Explicitly unload the extension page as soon as an update is available, // Explicitly unload the extension page as soon as an update is available,
// so the update gets installed as soon as possible. // so the update gets installed as soon as possible.
chrome.runtime.onUpdateAvailable.addListener(details => { chrome.runtime.onUpdateAvailable.addListener((details) => {
console.log("update available:", details); console.log("update available:", details);
chrome.runtime.reload(); chrome.runtime.reload();
}); });
chrome.tabs.query({}, tabs => { chrome.tabs.query({}, (tabs) => {
console.log("got tabs", tabs); console.log("got tabs", tabs);
for (const tab of tabs) { for (const tab of tabs) {
if (!tab.url || !tab.id) { if (!tab.url || !tab.id) {
@ -520,7 +520,7 @@ export async function wxMain() {
const run = () => { const run = () => {
timers.shift(); timers.shift();
chrome.tabs.get(tabId, tab => { chrome.tabs.get(tabId, (tab) => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
return; return;
} }
@ -562,7 +562,7 @@ export async function wxMain() {
// Handlers for catching HTTP requests // Handlers for catching HTTP requests
chrome.webRequest.onHeadersReceived.addListener( chrome.webRequest.onHeadersReceived.addListener(
details => { (details) => {
const wallet = currentWallet; const wallet = currentWallet;
if (!wallet) { if (!wallet) {
console.warn("wallet not available while handling header"); console.warn("wallet not available while handling header");