re-format with prettier v2, fix HTML
This commit is contained in:
parent
15e18440db
commit
aaf950e2ad
@ -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",
|
||||||
|
@ -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);
|
||||||
|
@ -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];
|
||||||
|
@ -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];
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
1493
src/i18n/strings.ts
1493
src/i18n/strings.ts
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
|
@ -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`);
|
||||||
|
@ -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 },
|
||||||
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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";
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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";
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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> {
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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(": ");
|
||||||
|
@ -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,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
};
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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) {
|
||||||
|
@ -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";
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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} />;
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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 />;
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
@ -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">
|
||||||
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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")!);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
@ -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")!);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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, "&").replace(/\\"/g, """)
|
.replace(/&/g, "&")
|
||||||
.replace(/</g, "<").replace(/>/g, ">")
|
.replace(/\\"/g, """)
|
||||||
.replace(jsonLine, replacer);
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.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);
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
@ -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);
|
||||||
|
@ -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(
|
||||||
|
@ -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");
|
||||||
|
Loading…
Reference in New Issue
Block a user