fix #7704
This commit is contained in:
parent
7330f0daf9
commit
d483a3f557
@ -725,6 +725,9 @@ export interface WalletCoreVersion {
|
||||
exchange: string;
|
||||
merchant: string;
|
||||
bank: string;
|
||||
/**
|
||||
* @deprecated will be removed
|
||||
*/
|
||||
devMode: boolean;
|
||||
}
|
||||
|
||||
@ -2157,15 +2160,6 @@ export interface ConfirmPeerPullDebitRequest {
|
||||
peerPullPaymentIncomingId: string;
|
||||
}
|
||||
|
||||
export interface SetDevModeRequest {
|
||||
devModeEnabled: boolean;
|
||||
}
|
||||
|
||||
export const codecForSetDevModeRequest = (): Codec<SetDevModeRequest> =>
|
||||
buildCodecForObject<SetDevModeRequest>()
|
||||
.property("devModeEnabled", codecForBoolean())
|
||||
.build("SetDevModeRequest");
|
||||
|
||||
export interface ApplyDevExperimentRequest {
|
||||
devExperimentUri: string;
|
||||
}
|
||||
|
@ -27,25 +27,18 @@ import {
|
||||
CoreApiResponse,
|
||||
Duration,
|
||||
encodeCrock,
|
||||
ExtendedStatus,
|
||||
getErrorDetailFromException,
|
||||
getRandomBytes,
|
||||
j2s,
|
||||
Logger,
|
||||
parsePaytoUri,
|
||||
PaymentStatus,
|
||||
PreparePayResultType,
|
||||
RecoveryMergeStrategy,
|
||||
RefreshReason,
|
||||
sampleWalletCoreTransactions,
|
||||
setDangerousTimetravel,
|
||||
setGlobalLogLevelFromString,
|
||||
summarizeTalerErrorDetail,
|
||||
TalerUriType,
|
||||
Transaction,
|
||||
TransactionState,
|
||||
TransactionSubstate,
|
||||
TransactionType,
|
||||
WalletNotification,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { clk } from "@gnu-taler/taler-util/clk";
|
||||
@ -61,15 +54,9 @@ import { JsonMessage, runRpcServer } from "@gnu-taler/taler-util/twrpc";
|
||||
import {
|
||||
createNativeWalletHost,
|
||||
createNativeWalletHost2,
|
||||
CryptoDispatcher,
|
||||
nativeCrypto,
|
||||
//NodeThreadCryptoWorkerFactory,
|
||||
//SynchronousCryptoWorkerFactoryPlain,
|
||||
TalerCryptoInterface,
|
||||
Wallet,
|
||||
WalletApiOperation,
|
||||
WalletCoreApiClient,
|
||||
walletCoreDebugFlags,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import {
|
||||
createRemoteWallet,
|
||||
@ -264,12 +251,19 @@ async function createLocalWallet(
|
||||
}
|
||||
},
|
||||
cryptoWorkerType: walletCliArgs.wallet.cryptoWorker as any,
|
||||
config: {
|
||||
features: {
|
||||
batchWithdrawal: checkEnvFlag("TALER_WALLET_BATCH_WITHDRAWAL"),
|
||||
},
|
||||
testing: {
|
||||
devModeActive: checkEnvFlag("TALER_WALLET_DEV_MODE"),
|
||||
denomselAllowLate: checkEnvFlag(
|
||||
"TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE",
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (checkEnvFlag("TALER_WALLET_BATCH_WITHDRAWAL")) {
|
||||
wallet.setBatchWithdrawal(true);
|
||||
}
|
||||
|
||||
applyVerbose(walletCliArgs.wallet.verbose);
|
||||
try {
|
||||
await wallet.handleCoreApiRequest("initWallet", "native-init", {
|
||||
@ -1405,30 +1399,6 @@ advancedCli
|
||||
});
|
||||
});
|
||||
|
||||
advancedCli
|
||||
.subcommand("enableDevMode", "enable-dev-mode", {
|
||||
help: "Enable developer mode (dangerous!)",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.client.call(WalletApiOperation.SetDevMode, {
|
||||
devModeEnabled: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
advancedCli
|
||||
.subcommand("disableDevMode", "disable-dev-mode", {
|
||||
help: "Disable developer mode",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.client.call(WalletApiOperation.SetDevMode, {
|
||||
devModeEnabled: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const coinPubListCodec = codecForList(codecForString());
|
||||
|
||||
advancedCli
|
||||
@ -1647,35 +1617,6 @@ async function read(stream: NodeJS.ReadStream) {
|
||||
return Buffer.concat(chunks).toString("utf8");
|
||||
}
|
||||
|
||||
// testCli
|
||||
// .subcommand("cryptoworker", "cryptoworker")
|
||||
// .maybeOption("impl", ["--impl"], clk.STRING)
|
||||
// .action(async (args) => {
|
||||
// let cryptoApi: TalerCryptoInterface;
|
||||
// if (!args.cryptoworker.impl || args.cryptoworker.impl === "node") {
|
||||
// const workerFactory = new NodeThreadCryptoWorkerFactory();
|
||||
// const cryptoDisp = new CryptoDispatcher(workerFactory);
|
||||
// cryptoApi = cryptoDisp.cryptoApi;
|
||||
// } else if (args.cryptoworker.impl === "sync") {
|
||||
// const workerFactory = new SynchronousCryptoWorkerFactoryPlain();
|
||||
// const cryptoDisp = new CryptoDispatcher(workerFactory);
|
||||
// cryptoApi = cryptoDisp.cryptoApi;
|
||||
// } else if (args.cryptoworker.impl === "none") {
|
||||
// cryptoApi = nativeCrypto;
|
||||
// } else {
|
||||
// throw Error(`invalid crypto worker type ${args.cryptoworker.impl}`);
|
||||
// }
|
||||
|
||||
// const input = "foo";
|
||||
// console.log(`testing crypto worker by hashing string '${input}'`);
|
||||
// const res = await cryptoApi.hashString({ str: input });
|
||||
// console.log(res);
|
||||
// });
|
||||
|
||||
export function main() {
|
||||
if (getenv("TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE")) {
|
||||
logger.warn("Allowing withdrawal of late denominations for debugging");
|
||||
walletCoreDebugFlags.denomselAllowLate = true;
|
||||
}
|
||||
walletCli.run();
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ import {
|
||||
} from "./operations/withdraw.js";
|
||||
import { ExchangeInfo } from "./operations/exchanges.js";
|
||||
import { assembleRefreshRevealRequest } from "./operations/refresh.js";
|
||||
import { isWithdrawableDenom } from "./index.js";
|
||||
import { isWithdrawableDenom, WalletConfig } from "./index.js";
|
||||
|
||||
const logger = new Logger("dbless.ts");
|
||||
|
||||
@ -206,6 +206,7 @@ export async function withdrawCoin(args: {
|
||||
}
|
||||
|
||||
export function findDenomOrThrow(
|
||||
denomselAllowLate: boolean,
|
||||
exchangeInfo: ExchangeInfo,
|
||||
amount: AmountString,
|
||||
): DenominationRecord {
|
||||
@ -215,7 +216,10 @@ export function findDenomOrThrow(
|
||||
fraction: d.amountFrac,
|
||||
value: d.amountVal,
|
||||
};
|
||||
if (Amounts.cmp(value, amount) === 0 && isWithdrawableDenom(d)) {
|
||||
if (
|
||||
Amounts.cmp(value, amount) === 0 &&
|
||||
isWithdrawableDenom(d, denomselAllowLate)
|
||||
) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
@ -36,35 +36,6 @@ import {
|
||||
|
||||
const logger = new Logger("dev-experiments.ts");
|
||||
|
||||
export async function setDevMode(
|
||||
ws: InternalWalletState,
|
||||
enabled: boolean,
|
||||
): Promise<void> {
|
||||
if (enabled) {
|
||||
logger.info("enabling devmode");
|
||||
await ws.db
|
||||
.mktx((x) => [x.config])
|
||||
.runReadWrite(async (tx) => {
|
||||
tx.config.put({
|
||||
key: ConfigRecordKey.DevMode,
|
||||
value: true,
|
||||
});
|
||||
});
|
||||
await maybeInitDevMode(ws);
|
||||
} else {
|
||||
logger.info("disabling devmode");
|
||||
await ws.db
|
||||
.mktx((x) => [x.config])
|
||||
.runReadWrite(async (tx) => {
|
||||
tx.config.put({
|
||||
key: ConfigRecordKey.DevMode,
|
||||
value: false,
|
||||
});
|
||||
});
|
||||
await leaveDevMode(ws);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a dev experiment to the wallet database / state.
|
||||
*/
|
||||
@ -78,7 +49,7 @@ export async function applyDevExperiment(
|
||||
logger.info("unable to parse dev experiment URI");
|
||||
return;
|
||||
}
|
||||
if (!ws.devModeActive) {
|
||||
if (!ws.config.testing.devModeActive) {
|
||||
throw Error(
|
||||
"can't handle devmode URI (other than enable-devmode) unless devmode is active",
|
||||
);
|
||||
@ -86,37 +57,6 @@ export async function applyDevExperiment(
|
||||
throw Error(`dev-experiment id not understood ${parsedUri.devExperimentId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter dev mode, if the wallet's config entry in the DB demands it.
|
||||
*/
|
||||
export async function maybeInitDevMode(ws: InternalWalletState): Promise<void> {
|
||||
const devMode = await ws.db
|
||||
.mktx((x) => [x.config])
|
||||
.runReadOnly(async (tx) => {
|
||||
const rec = await tx.config.get(ConfigRecordKey.DevMode);
|
||||
if (!rec || rec.key !== ConfigRecordKey.DevMode) {
|
||||
return false;
|
||||
}
|
||||
return rec.value;
|
||||
});
|
||||
if (!devMode) {
|
||||
ws.devModeActive = false;
|
||||
return;
|
||||
}
|
||||
ws.devModeActive = true;
|
||||
if (ws.http instanceof DevExperimentHttpLib) {
|
||||
return;
|
||||
}
|
||||
ws.http = new DevExperimentHttpLib(ws.http);
|
||||
}
|
||||
|
||||
export async function leaveDevMode(ws: InternalWalletState): Promise<void> {
|
||||
if (ws.http instanceof DevExperimentHttpLib) {
|
||||
ws.http = ws.http.underlyingLib;
|
||||
}
|
||||
ws.devModeActive = false;
|
||||
}
|
||||
|
||||
export class DevExperimentHttpLib implements HttpRequestLibrary {
|
||||
_isDevExperimentLib = true;
|
||||
underlyingLib: HttpRequestLibrary;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import { WalletNotification } from "@gnu-taler/taler-util";
|
||||
import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
|
||||
import { WalletConfig, WalletConfigParameter } from "./index.js";
|
||||
|
||||
/**
|
||||
* Helpers to initiate a wallet in a host environment.
|
||||
@ -44,6 +45,11 @@ export interface DefaultNodeWalletArgs {
|
||||
httpLib?: HttpRequestLibrary;
|
||||
|
||||
cryptoWorkerType?: "sync" | "node-worker-thread";
|
||||
|
||||
/**
|
||||
* Config parameters
|
||||
*/
|
||||
config?: WalletConfigParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +144,13 @@ export async function createNativeWalletHost2(
|
||||
|
||||
const timer = new SetTimeoutTimerAPI();
|
||||
|
||||
const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory);
|
||||
const w = await Wallet.create(
|
||||
myDb,
|
||||
myHttpLib,
|
||||
timer,
|
||||
workerFactory,
|
||||
args.config,
|
||||
);
|
||||
|
||||
if (args.notifyHandler) {
|
||||
w.addNotificationListener(args.notifyHandler);
|
||||
|
@ -108,7 +108,13 @@ export async function createNativeWalletHost2(
|
||||
|
||||
const timer = new SetTimeoutTimerAPI();
|
||||
|
||||
const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory);
|
||||
const w = await Wallet.create(
|
||||
myDb,
|
||||
myHttpLib,
|
||||
timer,
|
||||
workerFactory,
|
||||
args.config,
|
||||
);
|
||||
|
||||
if (args.notifyHandler) {
|
||||
w.addNotificationListener(args.notifyHandler);
|
||||
|
@ -36,7 +36,6 @@ export {
|
||||
|
||||
export * from "./pending-types.js";
|
||||
|
||||
export * from "./util/debugFlags.js";
|
||||
export { InternalWalletState } from "./internal-wallet-state.js";
|
||||
export * from "./wallet-api-types.js";
|
||||
export * from "./wallet.js";
|
||||
|
@ -52,6 +52,7 @@ import {
|
||||
GetReadWriteAccess,
|
||||
} from "./util/query.js";
|
||||
import { TimerGroup } from "./util/timer.js";
|
||||
import { WalletConfig } from "./wallet-api-types.js";
|
||||
|
||||
export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
|
||||
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
|
||||
@ -168,9 +169,7 @@ export interface InternalWalletState {
|
||||
timerGroup: TimerGroup;
|
||||
stopped: boolean;
|
||||
|
||||
insecureTrustExchange: boolean;
|
||||
|
||||
batchWithdrawal: boolean;
|
||||
config: Readonly<WalletConfig>;
|
||||
|
||||
/**
|
||||
* Asynchronous condition to interrupt the sleep of the
|
||||
@ -191,8 +190,6 @@ export interface InternalWalletState {
|
||||
merchantOps: MerchantOperations;
|
||||
refreshOps: RefreshOperations;
|
||||
|
||||
devModeActive: boolean;
|
||||
|
||||
getDenomInfo(
|
||||
ws: InternalWalletState,
|
||||
tx: GetReadOnlyAccess<{
|
||||
|
@ -840,7 +840,12 @@ export async function getTotalFeesForDepositAmount(
|
||||
denom.value,
|
||||
pcs.coinContributions[i],
|
||||
).amount;
|
||||
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
|
||||
const refreshCost = getTotalRefreshCost(
|
||||
allDenoms,
|
||||
denom,
|
||||
amountLeft,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
refreshFee.push(refreshCost);
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,7 @@ async function validateWireInfo(
|
||||
for (const a of wireInfo.accounts) {
|
||||
logger.trace("validating exchange acct");
|
||||
let isValid = false;
|
||||
if (ws.insecureTrustExchange) {
|
||||
if (ws.config.testing.insecureTrustExchange) {
|
||||
isValid = true;
|
||||
} else {
|
||||
const { valid: v } = await ws.cryptoApi.isValidWireAccount({
|
||||
@ -275,7 +275,7 @@ async function validateWireInfo(
|
||||
wireFee: Amounts.stringify(x.wire_fee),
|
||||
};
|
||||
let isValid = false;
|
||||
if (ws.insecureTrustExchange) {
|
||||
if (ws.config.testing.insecureTrustExchange) {
|
||||
isValid = true;
|
||||
} else {
|
||||
const { valid: v } = await ws.cryptoApi.isValidWireFee({
|
||||
@ -308,7 +308,7 @@ async function validateGlobalFees(
|
||||
for (const gf of fees) {
|
||||
logger.trace("validating exchange global fees");
|
||||
let isValid = false;
|
||||
if (ws.insecureTrustExchange) {
|
||||
if (ws.config.testing.insecureTrustExchange) {
|
||||
isValid = true;
|
||||
} else {
|
||||
const { valid: v } = await ws.cryptoApi.isValidGlobalFees({
|
||||
@ -665,7 +665,10 @@ export async function updateExchangeFromUrlHandler(
|
||||
|
||||
let ageMask = 0;
|
||||
for (const x of keysInfo.currentDenominations) {
|
||||
if (isWithdrawableDenom(x) && x.denomPub.age_mask != 0) {
|
||||
if (
|
||||
isWithdrawableDenom(x, ws.config.testing.denomselAllowLate) &&
|
||||
x.denomPub.age_mask != 0
|
||||
) {
|
||||
ageMask = x.denomPub.age_mask;
|
||||
break;
|
||||
}
|
||||
|
@ -168,6 +168,7 @@ export async function getTotalPaymentCost(
|
||||
allDenoms,
|
||||
DenominationRecord.toDenomInfo(denom),
|
||||
amountLeft,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
costs.push(Amounts.parseOrThrow(pcs.coinContributions[i]));
|
||||
costs.push(refreshCost);
|
||||
@ -1659,6 +1660,7 @@ async function applySuccessfulRefund(
|
||||
p: PurchaseRecord,
|
||||
refreshCoinsMap: Record<string, CoinRefreshRequest>,
|
||||
r: MerchantCoinRefundSuccessStatus,
|
||||
denomselAllowLate: boolean,
|
||||
): Promise<void> {
|
||||
// FIXME: check signature before storing it as valid!
|
||||
|
||||
@ -1688,6 +1690,7 @@ async function applySuccessfulRefund(
|
||||
allDenoms,
|
||||
DenominationRecord.toDenomInfo(denom),
|
||||
amountLeft,
|
||||
denomselAllowLate,
|
||||
);
|
||||
|
||||
refreshCoinsMap[coin.coinPub] = {
|
||||
@ -1714,6 +1717,7 @@ async function storePendingRefund(
|
||||
}>,
|
||||
p: PurchaseRecord,
|
||||
r: MerchantCoinRefundFailureStatus,
|
||||
denomselAllowLate: boolean,
|
||||
): Promise<void> {
|
||||
const refundKey = getRefundKey(r);
|
||||
|
||||
@ -1745,6 +1749,7 @@ async function storePendingRefund(
|
||||
allDenoms,
|
||||
DenominationRecord.toDenomInfo(denom),
|
||||
amountLeft,
|
||||
denomselAllowLate,
|
||||
);
|
||||
|
||||
p.refunds[refundKey] = {
|
||||
@ -1767,6 +1772,7 @@ async function storeFailedRefund(
|
||||
p: PurchaseRecord,
|
||||
refreshCoinsMap: Record<string, CoinRefreshRequest>,
|
||||
r: MerchantCoinRefundFailureStatus,
|
||||
denomselAllowLate: boolean,
|
||||
): Promise<void> {
|
||||
const refundKey = getRefundKey(r);
|
||||
|
||||
@ -1797,6 +1803,7 @@ async function storeFailedRefund(
|
||||
allDenoms,
|
||||
DenominationRecord.toDenomInfo(denom),
|
||||
amountLeft,
|
||||
denomselAllowLate,
|
||||
);
|
||||
|
||||
p.refunds[refundKey] = {
|
||||
@ -1905,11 +1912,28 @@ async function acceptRefunds(
|
||||
// Invariant: (!existingRefundInfo) || (existingRefundInfo === Pending)
|
||||
|
||||
if (refundStatus.type === "success") {
|
||||
await applySuccessfulRefund(tx, p, refreshCoinsMap, refundStatus);
|
||||
await applySuccessfulRefund(
|
||||
tx,
|
||||
p,
|
||||
refreshCoinsMap,
|
||||
refundStatus,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
} else if (isPermanentFailure) {
|
||||
await storeFailedRefund(tx, p, refreshCoinsMap, refundStatus);
|
||||
await storeFailedRefund(
|
||||
tx,
|
||||
p,
|
||||
refreshCoinsMap,
|
||||
refundStatus,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
} else {
|
||||
await storePendingRefund(tx, p, refundStatus);
|
||||
await storePendingRefund(
|
||||
tx,
|
||||
p,
|
||||
refundStatus,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +417,7 @@ export async function getTotalPeerPaymentCost(
|
||||
allDenoms,
|
||||
DenominationRecord.toDenomInfo(denom),
|
||||
amountLeft,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
costs.push(Amounts.parseOrThrow(pcs[i].contribution));
|
||||
costs.push(refreshCost);
|
||||
|
@ -86,7 +86,7 @@ import {
|
||||
import { makeCoinAvailable } from "./common.js";
|
||||
import { updateExchangeFromUrl } from "./exchanges.js";
|
||||
import { selectWithdrawalDenominations } from "../util/coinSelection.js";
|
||||
import { isWithdrawableDenom } from "../index.js";
|
||||
import { isWithdrawableDenom, WalletConfig } from "../index.js";
|
||||
|
||||
const logger = new Logger("refresh.ts");
|
||||
|
||||
@ -105,13 +105,18 @@ export function getTotalRefreshCost(
|
||||
denoms: DenominationRecord[],
|
||||
refreshedDenom: DenominationInfo,
|
||||
amountLeft: AmountJson,
|
||||
denomselAllowLate: boolean,
|
||||
): AmountJson {
|
||||
const withdrawAmount = Amounts.sub(
|
||||
amountLeft,
|
||||
refreshedDenom.feeRefresh,
|
||||
).amount;
|
||||
const denomMap = Object.fromEntries(denoms.map((x) => [x.denomPubHash, x]));
|
||||
const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, denoms);
|
||||
const withdrawDenoms = selectWithdrawalDenominations(
|
||||
withdrawAmount,
|
||||
denoms,
|
||||
denomselAllowLate,
|
||||
);
|
||||
const resultingAmount = Amounts.add(
|
||||
Amounts.zeroOfCurrency(withdrawAmount.currency),
|
||||
...withdrawDenoms.selectedDenoms.map(
|
||||
@ -232,6 +237,7 @@ async function refreshCreateSession(
|
||||
const newCoinDenoms = selectWithdrawalDenominations(
|
||||
availableAmount,
|
||||
availableDenoms,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
|
||||
if (newCoinDenoms.selectedDenoms.length === 0) {
|
||||
@ -897,7 +903,7 @@ export async function createRefreshGroup(
|
||||
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
|
||||
.iter(exchangeBaseUrl)
|
||||
.filter((x) => {
|
||||
return isWithdrawableDenom(x);
|
||||
return isWithdrawableDenom(x, ws.config.testing.denomselAllowLate);
|
||||
});
|
||||
denomsPerExchange[exchangeBaseUrl] = allDenoms;
|
||||
return allDenoms;
|
||||
@ -955,6 +961,7 @@ export async function createRefreshGroup(
|
||||
denoms,
|
||||
denom,
|
||||
Amounts.parseOrThrow(refreshAmount),
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
const output = Amounts.sub(refreshAmount, cost).amount;
|
||||
estimatedOutputPerCoin.push(output);
|
||||
|
@ -210,7 +210,9 @@ export async function getCandidateWithdrawalDenoms(
|
||||
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
|
||||
exchangeBaseUrl,
|
||||
);
|
||||
return allDenoms.filter(isWithdrawableDenom);
|
||||
return allDenoms.filter((d) =>
|
||||
isWithdrawableDenom(d, ws.config.testing.denomselAllowLate),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -719,7 +721,7 @@ export async function updateWithdrawalDenoms(
|
||||
}) signature of ${denom.denomPubHash}`,
|
||||
);
|
||||
let valid = false;
|
||||
if (ws.insecureTrustExchange) {
|
||||
if (ws.config.testing.insecureTrustExchange) {
|
||||
valid = true;
|
||||
} else {
|
||||
const res = await ws.cryptoApi.isValidDenom({
|
||||
@ -1003,7 +1005,7 @@ export async function processWithdrawalGroup(
|
||||
const resp = await processPlanchetExchangeBatchRequest(ws, wgContext, {
|
||||
batchSize: maxBatchSize,
|
||||
coinStartIndex: i,
|
||||
useBatchRequest: ws.batchWithdrawal,
|
||||
useBatchRequest: ws.config.features.batchWithdrawal,
|
||||
});
|
||||
let work: Promise<void>[] = [];
|
||||
work = [];
|
||||
@ -1180,6 +1182,7 @@ export async function getExchangeWithdrawalInfo(
|
||||
const selectedDenoms = selectWithdrawalDenominations(
|
||||
instructedAmount,
|
||||
denoms,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
|
||||
if (selectedDenoms.selectedDenoms.length === 0) {
|
||||
@ -1710,9 +1713,14 @@ export async function internalCreateWithdrawalGroup(
|
||||
amount,
|
||||
denoms,
|
||||
args.forcedDenomSel,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
} else {
|
||||
initialDenomSel = selectWithdrawalDenominations(amount, denoms);
|
||||
initialDenomSel = selectWithdrawalDenominations(
|
||||
amount,
|
||||
denoms,
|
||||
ws.config.testing.denomselAllowLate,
|
||||
);
|
||||
}
|
||||
|
||||
const withdrawalGroup: WithdrawalGroupRecord = {
|
||||
|
@ -48,7 +48,11 @@ import {
|
||||
AllowedExchangeInfo,
|
||||
DenominationRecord,
|
||||
} from "../db.js";
|
||||
import { getExchangeDetails, isWithdrawableDenom } from "../index.js";
|
||||
import {
|
||||
getExchangeDetails,
|
||||
isWithdrawableDenom,
|
||||
WalletConfig,
|
||||
} from "../index.js";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
import { getMerchantPaymentBalanceDetails } from "../operations/balance.js";
|
||||
import { checkDbInvariant, checkLogicInvariant } from "./invariants.js";
|
||||
@ -664,6 +668,7 @@ export async function selectCandidates(
|
||||
export function selectWithdrawalDenominations(
|
||||
amountAvailable: AmountJson,
|
||||
denoms: DenominationRecord[],
|
||||
denomselAllowLate: boolean = false,
|
||||
): DenomSelectionState {
|
||||
let remaining = Amounts.copy(amountAvailable);
|
||||
|
||||
@ -675,7 +680,7 @@ export function selectWithdrawalDenominations(
|
||||
let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency);
|
||||
let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
|
||||
|
||||
denoms = denoms.filter(isWithdrawableDenom);
|
||||
denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate));
|
||||
denoms.sort((d1, d2) =>
|
||||
Amounts.cmp(
|
||||
DenominationRecord.getValue(d2),
|
||||
@ -737,6 +742,7 @@ export function selectForcedWithdrawalDenominations(
|
||||
amountAvailable: AmountJson,
|
||||
denoms: DenominationRecord[],
|
||||
forcedDenomSel: ForcedDenomSel,
|
||||
denomselAllowLate: boolean,
|
||||
): DenomSelectionState {
|
||||
const selectedDenoms: {
|
||||
count: number;
|
||||
@ -746,7 +752,7 @@ export function selectForcedWithdrawalDenominations(
|
||||
let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency);
|
||||
let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
|
||||
|
||||
denoms = denoms.filter(isWithdrawableDenom);
|
||||
denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate));
|
||||
denoms.sort((d1, d2) =>
|
||||
Amounts.cmp(
|
||||
DenominationRecord.getValue(d2),
|
||||
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Debug flags for wallet-core.
|
||||
*
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
export interface WalletCoreDebugFlags {
|
||||
/**
|
||||
* Allow withdrawal of denominations even though they are about to expire.
|
||||
*/
|
||||
denomselAllowLate: boolean;
|
||||
}
|
||||
|
||||
export const walletCoreDebugFlags: WalletCoreDebugFlags = {
|
||||
denomselAllowLate: false,
|
||||
};
|
@ -29,7 +29,7 @@ import {
|
||||
WireFee,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { DenominationRecord } from "../db.js";
|
||||
import { walletCoreDebugFlags } from "./debugFlags.js";
|
||||
import { WalletConfig } from "../index.js";
|
||||
|
||||
/**
|
||||
* Given a list of denominations with the same value and same period of time:
|
||||
@ -452,13 +452,16 @@ export function createTimeline<Type extends object>(
|
||||
* Check if a denom is withdrawable based on the expiration time,
|
||||
* revocation and offered state.
|
||||
*/
|
||||
export function isWithdrawableDenom(d: DenominationRecord): boolean {
|
||||
export function isWithdrawableDenom(
|
||||
d: DenominationRecord,
|
||||
denomselAllowLate?: boolean,
|
||||
): boolean {
|
||||
const now = AbsoluteTime.now();
|
||||
const start = AbsoluteTime.fromTimestamp(d.stampStart);
|
||||
const withdrawExpire = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||
const started = AbsoluteTime.cmp(now, start) >= 0;
|
||||
let lastPossibleWithdraw: AbsoluteTime;
|
||||
if (walletCoreDebugFlags.denomselAllowLate) {
|
||||
if (denomselAllowLate) {
|
||||
lastPossibleWithdraw = start;
|
||||
} else {
|
||||
lastPossibleWithdraw = AbsoluteTime.subtractDuraction(
|
||||
|
@ -29,8 +29,6 @@ import {
|
||||
AcceptExchangeTosRequest,
|
||||
AcceptManualWithdrawalRequest,
|
||||
AcceptManualWithdrawalResult,
|
||||
ConfirmPeerPullDebitRequest,
|
||||
ConfirmPeerPushCreditRequest,
|
||||
AcceptTipRequest,
|
||||
AcceptTipResponse,
|
||||
AcceptWithdrawalResponse,
|
||||
@ -42,17 +40,18 @@ import {
|
||||
ApplyRefundResponse,
|
||||
BackupRecovery,
|
||||
BalancesResponse,
|
||||
PreparePeerPullDebitRequest,
|
||||
PreparePeerPullDebitResponse,
|
||||
PreparePeerPushCredit,
|
||||
PreparePeerPushCreditResponse,
|
||||
CheckPeerPullCreditRequest,
|
||||
CheckPeerPullCreditResponse,
|
||||
CheckPeerPushDebitRequest,
|
||||
CheckPeerPushDebitResponse,
|
||||
CoinDumpJson,
|
||||
ConfirmPayRequest,
|
||||
ConfirmPayResult,
|
||||
ConfirmPeerPullDebitRequest,
|
||||
ConfirmPeerPushCreditRequest,
|
||||
CreateDepositGroupRequest,
|
||||
CreateDepositGroupResponse,
|
||||
DeleteTransactionRequest,
|
||||
DepositGroupFees,
|
||||
ExchangeDetailedResponse,
|
||||
ExchangesListResponse,
|
||||
ForceRefreshRequest,
|
||||
@ -63,12 +62,12 @@ import {
|
||||
GetExchangeTosResult,
|
||||
GetWithdrawalDetailsForAmountRequest,
|
||||
GetWithdrawalDetailsForUriRequest,
|
||||
InitRequest,
|
||||
InitResponse,
|
||||
InitiatePeerPullCreditRequest,
|
||||
InitiatePeerPullCreditResponse,
|
||||
InitiatePeerPushPaymentRequest,
|
||||
InitiatePeerPushPaymentResponse,
|
||||
InitRequest,
|
||||
InitResponse,
|
||||
IntegrationTestArgs,
|
||||
KnownBankAccounts,
|
||||
ListKnownBankAccountsRequest,
|
||||
@ -78,10 +77,10 @@ import {
|
||||
PreparePayRequest,
|
||||
PreparePayResult,
|
||||
PreparePayTemplateRequest,
|
||||
CheckPeerPullCreditRequest,
|
||||
CheckPeerPullCreditResponse,
|
||||
CheckPeerPushDebitRequest,
|
||||
CheckPeerPushDebitResponse,
|
||||
PreparePeerPullDebitRequest,
|
||||
PreparePeerPullDebitResponse,
|
||||
PreparePeerPushCredit,
|
||||
PreparePeerPushCreditResponse,
|
||||
PrepareRefundRequest,
|
||||
PrepareRefundResult,
|
||||
PrepareTipRequest,
|
||||
@ -89,7 +88,6 @@ import {
|
||||
RecoveryLoadRequest,
|
||||
RetryTransactionRequest,
|
||||
SetCoinSuspendedRequest,
|
||||
SetDevModeRequest,
|
||||
SetWalletDeviceIdRequest,
|
||||
TestPayArgs,
|
||||
TestPayResult,
|
||||
@ -97,21 +95,21 @@ import {
|
||||
TransactionByIdRequest,
|
||||
TransactionsRequest,
|
||||
TransactionsResponse,
|
||||
TxIdResponse,
|
||||
UserAttentionByIdRequest,
|
||||
UserAttentionsCountResponse,
|
||||
UserAttentionsRequest,
|
||||
UserAttentionsResponse,
|
||||
ValidateIbanRequest,
|
||||
ValidateIbanResponse,
|
||||
WalletBackupContentV1,
|
||||
WalletCoreVersion,
|
||||
WalletCurrencyInfo,
|
||||
WithdrawFakebankRequest,
|
||||
WithdrawTestBalanceRequest,
|
||||
WithdrawUriInfoResponse,
|
||||
ValidateIbanRequest,
|
||||
ValidateIbanResponse,
|
||||
TxIdResponse,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { WalletContractData } from "./db.js";
|
||||
import { AuditorTrustRecord, WalletContractData } from "./db.js";
|
||||
import {
|
||||
AddBackupProviderRequest,
|
||||
AddBackupProviderResponse,
|
||||
@ -195,13 +193,11 @@ export enum WalletApiOperation {
|
||||
ConfirmPeerPullDebit = "confirmPeerPullDebit",
|
||||
ClearDb = "clearDb",
|
||||
Recycle = "recycle",
|
||||
SetDevMode = "setDevMode",
|
||||
ApplyDevExperiment = "applyDevExperiment",
|
||||
ValidateIban = "validateIban",
|
||||
}
|
||||
|
||||
// group: Initialization
|
||||
|
||||
type EmptyObject = Record<string, never>;
|
||||
/**
|
||||
* Initialize wallet-core.
|
||||
@ -739,12 +735,6 @@ export type ApplyDevExperimentOp = {
|
||||
response: EmptyObject;
|
||||
};
|
||||
|
||||
export type SetDevModeOp = {
|
||||
op: WalletApiOperation.SetDevMode;
|
||||
request: SetDevModeRequest;
|
||||
response: EmptyObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a simple integration test on a test deployment
|
||||
* of the exchange and merchant.
|
||||
@ -953,7 +943,6 @@ export type WalletOperations = {
|
||||
[WalletApiOperation.ClearDb]: ClearDbOp;
|
||||
[WalletApiOperation.Recycle]: RecycleOp;
|
||||
[WalletApiOperation.ApplyDevExperiment]: ApplyDevExperimentOp;
|
||||
[WalletApiOperation.SetDevMode]: SetDevModeOp;
|
||||
[WalletApiOperation.ValidateIban]: ValidateIbanOp;
|
||||
};
|
||||
|
||||
@ -973,3 +962,50 @@ export interface WalletCoreApiClient {
|
||||
payload: WalletCoreRequestType<Op>,
|
||||
): Promise<WalletCoreResponseType<Op>>;
|
||||
}
|
||||
|
||||
type Primitives = string | number | boolean;
|
||||
|
||||
export type RecursivePartial<T extends object> = {
|
||||
[P in keyof T]?: T[P] extends Array<infer U extends object>
|
||||
? Array<RecursivePartial<U>>
|
||||
: T[P] extends Array<infer J extends Primitives>
|
||||
? Array<J>
|
||||
: T[P] extends object
|
||||
? RecursivePartial<T[P]>
|
||||
: T[P];
|
||||
} & object;
|
||||
|
||||
export type WalletConfigParameter = RecursivePartial<WalletConfig>;
|
||||
|
||||
export interface WalletConfig {
|
||||
/**
|
||||
* Initialization values useful for a complete startup.
|
||||
*
|
||||
* These are values may be overridden by different wallets
|
||||
*/
|
||||
builtin: {
|
||||
exchanges: string[];
|
||||
auditors: AuditorTrustRecord[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Unsafe options which it should only be used to create
|
||||
* testing environment.
|
||||
*/
|
||||
testing: {
|
||||
/**
|
||||
* Allow withdrawal of denominations even though they are about to expire.
|
||||
*/
|
||||
denomselAllowLate: boolean;
|
||||
devModeActive: boolean;
|
||||
insecureTrustExchange: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configurations values that may be safe to show to the user
|
||||
*/
|
||||
features: {
|
||||
batchWithdrawal: boolean;
|
||||
allowHttp: boolean;
|
||||
};
|
||||
}
|
||||
|
@ -25,12 +25,38 @@
|
||||
import {
|
||||
AbsoluteTime,
|
||||
Amounts,
|
||||
CoinDumpJson,
|
||||
CoinRefreshRequest,
|
||||
CoinStatus,
|
||||
CoreApiResponse,
|
||||
DenomOperationMap,
|
||||
DenominationInfo,
|
||||
Duration,
|
||||
ExchangeDetailedResponse,
|
||||
ExchangeListItem,
|
||||
ExchangeTosStatusDetails,
|
||||
ExchangesListResponse,
|
||||
FeeDescription,
|
||||
GetExchangeTosResult,
|
||||
InitResponse,
|
||||
KnownBankAccounts,
|
||||
KnownBankAccountsInfo,
|
||||
Logger,
|
||||
ManualWithdrawalDetails,
|
||||
MerchantUsingTemplateDetails,
|
||||
NotificationType,
|
||||
RefreshReason,
|
||||
TalerError,
|
||||
TalerErrorCode,
|
||||
URL,
|
||||
ValidateIbanResponse,
|
||||
WalletCoreVersion,
|
||||
WalletNotification,
|
||||
codecForAbortTransaction,
|
||||
codecForAcceptBankIntegratedWithdrawalRequest,
|
||||
codecForAcceptExchangeTosRequest,
|
||||
codecForAcceptManualWithdrawalRequet,
|
||||
codecForAcceptPeerPullPaymentRequest,
|
||||
codecForConfirmPeerPushPaymentRequest,
|
||||
codecForAcceptTipRequest,
|
||||
codecForAddExchangeRequest,
|
||||
codecForAddKnownBankAccounts,
|
||||
@ -39,8 +65,9 @@ import {
|
||||
codecForApplyRefundFromPurchaseIdRequest,
|
||||
codecForApplyRefundRequest,
|
||||
codecForCheckPeerPullPaymentRequest,
|
||||
codecForPreparePeerPushCreditRequest,
|
||||
codecForCheckPeerPushDebitRequest,
|
||||
codecForConfirmPayRequest,
|
||||
codecForConfirmPeerPushPaymentRequest,
|
||||
codecForCreateDepositGroupRequest,
|
||||
codecForDeleteTransactionRequest,
|
||||
codecForForceRefreshRequest,
|
||||
@ -54,86 +81,58 @@ import {
|
||||
codecForInitiatePeerPullPaymentRequest,
|
||||
codecForInitiatePeerPushPaymentRequest,
|
||||
codecForIntegrationTestArgs,
|
||||
codecForIntegrationTestV2Args,
|
||||
codecForListKnownBankAccounts,
|
||||
codecForMerchantPostOrderResponse,
|
||||
codecForPrepareDepositRequest,
|
||||
codecForPreparePayRequest,
|
||||
codecForPreparePayTemplateRequest,
|
||||
codecForPreparePeerPullPaymentRequest,
|
||||
codecForCheckPeerPushDebitRequest,
|
||||
codecForPreparePeerPushCreditRequest,
|
||||
codecForPrepareRefundRequest,
|
||||
codecForPrepareTipRequest,
|
||||
codecForResumeTransaction,
|
||||
codecForRetryTransactionRequest,
|
||||
codecForSetCoinSuspendedRequest,
|
||||
codecForSetDevModeRequest,
|
||||
codecForSetWalletDeviceIdRequest,
|
||||
codecForSuspendTransaction,
|
||||
codecForTestPayArgs,
|
||||
codecForTransactionByIdRequest,
|
||||
codecForTransactionsRequest,
|
||||
codecForUserAttentionByIdRequest,
|
||||
codecForUserAttentionsRequest,
|
||||
codecForValidateIbanRequest,
|
||||
codecForWithdrawFakebankRequest,
|
||||
codecForWithdrawTestBalance,
|
||||
CoinDumpJson,
|
||||
CoinRefreshRequest,
|
||||
CoinStatus,
|
||||
constructPayUri,
|
||||
CoreApiResponse,
|
||||
DenominationInfo,
|
||||
DenomOperationMap,
|
||||
Duration,
|
||||
durationFromSpec,
|
||||
durationMin,
|
||||
ExchangeDetailedResponse,
|
||||
ExchangeListItem,
|
||||
ExchangesListResponse,
|
||||
ExchangeTosStatusDetails,
|
||||
FeeDescription,
|
||||
GetExchangeTosResult,
|
||||
InitResponse,
|
||||
getErrorDetailFromException,
|
||||
j2s,
|
||||
KnownBankAccounts,
|
||||
KnownBankAccountsInfo,
|
||||
Logger,
|
||||
ManualWithdrawalDetails,
|
||||
MerchantUsingTemplateDetails,
|
||||
NotificationType,
|
||||
parsePayTemplateUri,
|
||||
parsePaytoUri,
|
||||
RefreshReason,
|
||||
TalerErrorCode,
|
||||
URL,
|
||||
WalletCoreVersion,
|
||||
WalletNotification,
|
||||
codecForSuspendTransaction,
|
||||
codecForResumeTransaction,
|
||||
validateIban,
|
||||
codecForValidateIbanRequest,
|
||||
ValidateIbanResponse,
|
||||
codecForIntegrationTestV2Args,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
||||
import {
|
||||
CryptoDispatcher,
|
||||
CryptoWorkerFactory,
|
||||
} from "./crypto/workers/crypto-dispatcher.js";
|
||||
import {
|
||||
AuditorTrustRecord,
|
||||
clearDatabase,
|
||||
CoinSourceType,
|
||||
ConfigRecordKey,
|
||||
DenominationRecord,
|
||||
ExchangeDetailsRecord,
|
||||
WalletStoresV1,
|
||||
clearDatabase,
|
||||
exportDb,
|
||||
importDb,
|
||||
WalletStoresV1,
|
||||
} from "./db.js";
|
||||
import {
|
||||
applyDevExperiment,
|
||||
maybeInitDevMode,
|
||||
setDevMode,
|
||||
} from "./dev-experiments.js";
|
||||
import { getErrorDetailFromException, TalerError } from "@gnu-taler/taler-util";
|
||||
import { DevExperimentHttpLib, applyDevExperiment } from "./dev-experiments.js";
|
||||
import {
|
||||
ActiveLongpollInfo,
|
||||
ExchangeOperations,
|
||||
@ -198,18 +197,18 @@ import {
|
||||
processPurchase,
|
||||
} from "./operations/pay-merchant.js";
|
||||
import {
|
||||
confirmPeerPullDebit,
|
||||
confirmPeerPushCredit,
|
||||
preparePeerPullDebit,
|
||||
preparePeerPushCredit,
|
||||
initiatePeerPullPayment,
|
||||
initiatePeerPushPayment,
|
||||
checkPeerPullPaymentInitiation,
|
||||
checkPeerPushDebit,
|
||||
confirmPeerPullDebit,
|
||||
confirmPeerPushCredit,
|
||||
initiatePeerPullPayment,
|
||||
initiatePeerPushPayment,
|
||||
preparePeerPullDebit,
|
||||
preparePeerPushCredit,
|
||||
processPeerPullCredit,
|
||||
processPeerPushInitiation,
|
||||
processPeerPullDebit,
|
||||
processPeerPushCredit,
|
||||
processPeerPushInitiation,
|
||||
} from "./operations/pay-peer.js";
|
||||
import { getPendingOperations } from "./operations/pending.js";
|
||||
import {
|
||||
@ -252,10 +251,6 @@ import {
|
||||
selectBestForOverlappingDenominations,
|
||||
selectMinimumFee,
|
||||
} from "./util/denominations.js";
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
import { checkDbInvariant } from "./util/invariants.js";
|
||||
import {
|
||||
AsyncCondition,
|
||||
@ -275,22 +270,14 @@ import {
|
||||
WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||
} from "./versions.js";
|
||||
import {
|
||||
RecursivePartial,
|
||||
WalletApiOperation,
|
||||
WalletConfig,
|
||||
WalletConfigParameter,
|
||||
WalletCoreApiClient,
|
||||
WalletCoreResponseType,
|
||||
} from "./wallet-api-types.js";
|
||||
|
||||
const builtinAuditors: AuditorTrustRecord[] = [
|
||||
{
|
||||
currency: "KUDOS",
|
||||
auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0",
|
||||
auditorBaseUrl: "https://auditor.demo.taler.net/",
|
||||
uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"],
|
||||
},
|
||||
];
|
||||
|
||||
const builtinExchanges: string[] = ["https://exchange.demo.taler.net/"];
|
||||
|
||||
const logger = new Logger("wallet.ts");
|
||||
|
||||
/**
|
||||
@ -495,10 +482,10 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> {
|
||||
return;
|
||||
}
|
||||
logger.info("importing default exchanges and auditors");
|
||||
for (const c of builtinAuditors) {
|
||||
for (const c of ws.config.builtin.auditors) {
|
||||
await tx.auditorTrust.put(c);
|
||||
}
|
||||
for (const baseUrl of builtinExchanges) {
|
||||
for (const baseUrl of ws.config.builtin.exchanges) {
|
||||
const now = AbsoluteTime.now();
|
||||
provideExchangeRecordInTx(ws, tx, baseUrl, now);
|
||||
}
|
||||
@ -1018,7 +1005,6 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
||||
logger.trace("filling defaults");
|
||||
await fillDefaults(ws);
|
||||
}
|
||||
await maybeInitDevMode(ws);
|
||||
const resp: InitResponse = {
|
||||
versionInfo: getVersion(ws),
|
||||
};
|
||||
@ -1482,15 +1468,10 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
||||
await applyDevExperiment(ws, req.devExperimentUri);
|
||||
return {};
|
||||
}
|
||||
case WalletApiOperation.SetDevMode: {
|
||||
const req = codecForSetDevModeRequest().decode(payload);
|
||||
await setDevMode(ws, req.devModeEnabled);
|
||||
return {};
|
||||
}
|
||||
case WalletApiOperation.GetVersion: {
|
||||
return getVersion(ws);
|
||||
}
|
||||
//default:
|
||||
// default:
|
||||
// assertUnreachable(operation);
|
||||
}
|
||||
throw TalerError.fromDetail(
|
||||
@ -1509,7 +1490,7 @@ export function getVersion(ws: InternalWalletState): WalletCoreVersion {
|
||||
exchange: WALLET_EXCHANGE_PROTOCOL_VERSION,
|
||||
merchant: WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||
bank: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
|
||||
devMode: ws.devModeActive,
|
||||
devMode: false,
|
||||
};
|
||||
return version;
|
||||
}
|
||||
@ -1575,8 +1556,15 @@ export class Wallet {
|
||||
http: HttpRequestLibrary,
|
||||
timer: TimerAPI,
|
||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||
config?: WalletConfigParameter,
|
||||
) {
|
||||
this.ws = new InternalWalletStateImpl(db, http, timer, cryptoWorkerFactory);
|
||||
this.ws = new InternalWalletStateImpl(
|
||||
db,
|
||||
http,
|
||||
timer,
|
||||
cryptoWorkerFactory,
|
||||
config ?? {},
|
||||
);
|
||||
}
|
||||
|
||||
get client(): WalletCoreApiClient {
|
||||
@ -1586,29 +1574,22 @@ export class Wallet {
|
||||
return this._client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trust the exchange, do not validate signatures.
|
||||
* Only used to benchmark the exchange.
|
||||
*/
|
||||
setInsecureTrustExchange(): void {
|
||||
this.ws.insecureTrustExchange = true;
|
||||
}
|
||||
|
||||
setBatchWithdrawal(enable: boolean): void {
|
||||
this.ws.batchWithdrawal = enable;
|
||||
}
|
||||
|
||||
static async create(
|
||||
db: DbAccess<typeof WalletStoresV1>,
|
||||
http: HttpRequestLibrary,
|
||||
timer: TimerAPI,
|
||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||
config?: WalletConfigParameter,
|
||||
): Promise<Wallet> {
|
||||
const w = new Wallet(db, http, timer, cryptoWorkerFactory);
|
||||
const w = new Wallet(db, http, timer, cryptoWorkerFactory, config);
|
||||
w._client = await getClientFromWalletState(w.ws);
|
||||
return w;
|
||||
}
|
||||
|
||||
static getDefaultConfig(): Readonly<WalletConfig> {
|
||||
return InternalWalletStateImpl.defaultConfig;
|
||||
}
|
||||
|
||||
addNotificationListener(f: (n: WalletNotification) => void): void {
|
||||
return this.ws.addNotificationListener(f);
|
||||
}
|
||||
@ -1650,10 +1631,6 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
|
||||
merchantInfoCache: Record<string, MerchantInfo> = {};
|
||||
|
||||
insecureTrustExchange = false;
|
||||
|
||||
batchWithdrawal = false;
|
||||
|
||||
readonly timerGroup: TimerGroup;
|
||||
latch = new AsyncCondition();
|
||||
stopped = false;
|
||||
@ -1662,8 +1639,6 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
|
||||
initCalled = false;
|
||||
|
||||
devModeActive = false;
|
||||
|
||||
exchangeOps: ExchangeOperations = {
|
||||
getExchangeDetails,
|
||||
getExchangeTrust,
|
||||
@ -1696,6 +1671,31 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
*/
|
||||
private resourceLocks: Set<string> = new Set();
|
||||
|
||||
config: Readonly<WalletConfig>;
|
||||
|
||||
public static defaultConfig: Readonly<WalletConfig> = {
|
||||
builtin: {
|
||||
exchanges: ["https://exchange.demo.taler.net/"],
|
||||
auditors: [
|
||||
{
|
||||
currency: "KUDOS",
|
||||
auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0",
|
||||
auditorBaseUrl: "https://auditor.demo.taler.net/",
|
||||
uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"],
|
||||
},
|
||||
],
|
||||
},
|
||||
features: {
|
||||
batchWithdrawal: false,
|
||||
allowHttp: false,
|
||||
},
|
||||
testing: {
|
||||
devModeActive: false,
|
||||
insecureTrustExchange: false,
|
||||
denomselAllowLate: false,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(
|
||||
// FIXME: Make this a getter and make
|
||||
// the actual value nullable.
|
||||
@ -1705,10 +1705,15 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
public http: HttpRequestLibrary,
|
||||
public timer: TimerAPI,
|
||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||
config: WalletConfigParameter,
|
||||
) {
|
||||
this.cryptoDispatcher = new CryptoDispatcher(cryptoWorkerFactory);
|
||||
this.cryptoApi = this.cryptoDispatcher.cryptoApi;
|
||||
this.timerGroup = new TimerGroup(timer);
|
||||
this.config = deepMerge(InternalWalletStateImpl.defaultConfig, config);
|
||||
if (this.config.testing.devModeActive) {
|
||||
this.http = new DevExperimentHttpLib(this.http);
|
||||
}
|
||||
}
|
||||
|
||||
async getDenomInfo(
|
||||
@ -1808,3 +1813,29 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the full object as template, create a new result with all the values.
|
||||
* Use the override object to change the values in the result
|
||||
* return result
|
||||
* @param full
|
||||
* @param override
|
||||
* @returns
|
||||
*/
|
||||
function deepMerge<T extends object>(
|
||||
full: T,
|
||||
override: RecursivePartial<T>,
|
||||
): T {
|
||||
const keys = Object.keys(full);
|
||||
const result = { ...full };
|
||||
for (const k of keys) {
|
||||
// @ts-ignore
|
||||
const newVal = override[k];
|
||||
if (newVal === undefined) continue;
|
||||
// @ts-ignore
|
||||
result[k] =
|
||||
// @ts-ignore
|
||||
typeof newVal === "object" ? deepMerge(full[k], newVal) : newVal;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
*/
|
||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { JustInDevMode } from "./components/JustInDevMode.js";
|
||||
import { EnabledBySettings } from "./components/EnabledBySettings.js";
|
||||
import {
|
||||
NavigationHeader,
|
||||
NavigationHeaderHolder,
|
||||
@ -183,11 +183,11 @@ export function PopupNavBar({ path }: { path?: PopupNavBarOptions }): VNode {
|
||||
<a href={Pages.balance} class={path === "balance" ? "active" : ""}>
|
||||
<i18n.Translate>Balance</i18n.Translate>
|
||||
</a>
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="backup">
|
||||
<a href={Pages.backup} class={path === "backup" ? "active" : ""}>
|
||||
<i18n.Translate>Backup</i18n.Translate>
|
||||
</a>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
<div style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}>
|
||||
{attentionCount > 0 ? (
|
||||
<a href={Pages.notifications}>
|
||||
@ -238,11 +238,11 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
|
||||
<a href={Pages.balance} class={path === "balance" ? "active" : ""}>
|
||||
<i18n.Translate>Balance</i18n.Translate>
|
||||
</a>
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="backup">
|
||||
<a href={Pages.backup} class={path === "backup" ? "active" : ""}>
|
||||
<i18n.Translate>Backup</i18n.Translate>
|
||||
</a>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
|
||||
{attentionCount > 0 ? (
|
||||
<a href={Pages.notifications}>
|
||||
@ -252,11 +252,11 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
|
||||
<Fragment />
|
||||
)}
|
||||
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="advanceMode">
|
||||
<a href={Pages.dev} class={path === "dev" ? "active" : ""}>
|
||||
<i18n.Translate>Dev</i18n.Translate>
|
||||
<i18n.Translate>Dev tools</i18n.Translate>
|
||||
</a>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
|
||||
<div
|
||||
style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}
|
||||
|
@ -27,7 +27,6 @@ import { platform, setupPlatform } from "./platform/background.js";
|
||||
import devAPI from "./platform/dev.js";
|
||||
import { wxMain } from "./wxBackend.js";
|
||||
|
||||
console.log("Wallet setup for Dev API");
|
||||
setupPlatform(devAPI);
|
||||
|
||||
async function start() {
|
||||
|
@ -35,10 +35,8 @@ const isFirefox =
|
||||
// FIXME: create different entry point for any platform instead of
|
||||
// switching in runtime
|
||||
if (isFirefox) {
|
||||
console.log("Wallet setup for Firefox API");
|
||||
setupPlatform(firefoxAPI);
|
||||
} else {
|
||||
console.log("Wallet setup for Chrome API");
|
||||
setupPlatform(chromeAPI);
|
||||
}
|
||||
|
||||
|
@ -14,14 +14,25 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
import { ComponentChildren, Fragment, h, VNode } from "preact";
|
||||
import { useDevContext } from "../context/devContext.js";
|
||||
import { useSettings } from "../hooks/useSettings.js";
|
||||
import { Settings } from "../platform/api.js";
|
||||
|
||||
export function JustInDevMode({
|
||||
export function EnabledBySettings<K extends keyof Settings>({
|
||||
children,
|
||||
value,
|
||||
name,
|
||||
}: {
|
||||
name: K;
|
||||
value?: Settings[K];
|
||||
children: ComponentChildren;
|
||||
}): VNode {
|
||||
const { devMode } = useDevContext();
|
||||
if (!devMode) return <Fragment />;
|
||||
const [settings] = useSettings();
|
||||
if (value === undefined) {
|
||||
if (!settings[name]) return <Fragment />;
|
||||
return <Fragment>{children}</Fragment>;
|
||||
}
|
||||
if (settings[name] !== value) {
|
||||
return <Fragment />;
|
||||
}
|
||||
return <Fragment>{children}</Fragment>;
|
||||
}
|
@ -17,8 +17,8 @@ import { TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import arrowDown from "../svg/chevron-down.svg";
|
||||
import { useDevContext } from "../context/devContext.js";
|
||||
import { ErrorBox } from "./styled/index.js";
|
||||
import { EnabledBySettings } from "./EnabledBySettings.js";
|
||||
|
||||
export function ErrorTalerOperation({
|
||||
title,
|
||||
@ -27,7 +27,6 @@ export function ErrorTalerOperation({
|
||||
title?: TranslatedString;
|
||||
error?: TalerErrorDetail;
|
||||
}): VNode | null {
|
||||
const { devMode } = useDevContext();
|
||||
const [showErrorDetail, setShowErrorDetail] = useState(false);
|
||||
|
||||
if (!title || !error) return null;
|
||||
@ -62,11 +61,11 @@ export function ErrorTalerOperation({
|
||||
<b>{error.hint}</b> {!errorHint ? "" : `: ${errorHint}`}{" "}
|
||||
</div>
|
||||
</div>
|
||||
{devMode && (
|
||||
<EnabledBySettings name="showJsonOnError">
|
||||
<div style={{ textAlign: "left", overflowX: "auto" }}>
|
||||
<pre>{JSON.stringify(error, undefined, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</EnabledBySettings>
|
||||
</Fragment>
|
||||
)}
|
||||
</ErrorBox>
|
||||
|
@ -14,7 +14,11 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { ExchangeTosStatus, GetExchangeTosResult } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
ExchangeTosStatus,
|
||||
GetExchangeTosResult,
|
||||
Logger,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
export function buildTermsOfServiceState(
|
||||
tos: GetExchangeTosResult,
|
||||
@ -27,6 +31,8 @@ export function buildTermsOfServiceState(
|
||||
return { content, status: tos.tosStatus, version: tos.currentEtag };
|
||||
}
|
||||
|
||||
const logger = new Logger("termsofservice");
|
||||
|
||||
function parseTermsOfServiceContent(
|
||||
type: string,
|
||||
text: string,
|
||||
@ -36,28 +42,28 @@ function parseTermsOfServiceContent(
|
||||
const document = new DOMParser().parseFromString(text, "text/xml");
|
||||
return { type: "xml", document };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
logger.error("error parsing xml", e);
|
||||
}
|
||||
} else if (type === "text/html") {
|
||||
try {
|
||||
const href = new URL(text);
|
||||
return { type: "html", href };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
logger.error("error parsing url", e);
|
||||
}
|
||||
} else if (type === "text/json") {
|
||||
try {
|
||||
const data = JSON.parse(text);
|
||||
return { type: "json", data };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
logger.error("error parsing json", e);
|
||||
}
|
||||
} else if (type === "text/pdf") {
|
||||
try {
|
||||
const location = new URL(text);
|
||||
return { type: "pdf", location };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
logger.error("error parsing url", e);
|
||||
}
|
||||
}
|
||||
const content = text;
|
||||
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { createContext, h, VNode } from "preact";
|
||||
import { useContext } from "preact/hooks";
|
||||
import { useWalletDevMode } from "../hooks/useWalletDevMode.js";
|
||||
|
||||
interface Type {
|
||||
devMode: boolean;
|
||||
toggle: () => Promise<void>;
|
||||
}
|
||||
const Context = createContext<Type>({
|
||||
devMode: false,
|
||||
toggle: async () => {
|
||||
null;
|
||||
},
|
||||
});
|
||||
|
||||
export const useDevContext = (): Type => useContext(Context);
|
||||
|
||||
export const DevContextProviderForTesting = ({
|
||||
value,
|
||||
children,
|
||||
}: {
|
||||
value?: boolean;
|
||||
children: any;
|
||||
}): VNode => {
|
||||
return h(Context.Provider, {
|
||||
value: {
|
||||
devMode: !!value,
|
||||
toggle: async () => {
|
||||
null;
|
||||
},
|
||||
},
|
||||
children,
|
||||
});
|
||||
};
|
||||
|
||||
export const DevContextProvider = ({ children }: { children: any }): VNode => {
|
||||
const devModeToggle = useWalletDevMode();
|
||||
const value: Type = {
|
||||
devMode: !!devModeToggle.value,
|
||||
toggle: devModeToggle.toggle,
|
||||
};
|
||||
//support for function as children, useful for getting the value right away
|
||||
children =
|
||||
children.length === 1 && typeof children === "function"
|
||||
? children(value)
|
||||
: children;
|
||||
|
||||
return h(Context.Provider, { value, children });
|
||||
};
|
@ -167,8 +167,6 @@ export function useComponentState({
|
||||
if (fu) {
|
||||
if (typeof window !== "undefined") {
|
||||
document.location.href = fu;
|
||||
} else {
|
||||
console.log(`should d to ${fu}`);
|
||||
}
|
||||
}
|
||||
onSuccess(res.transactionId);
|
||||
|
@ -30,7 +30,6 @@ export function ReadyView({
|
||||
}: State.FillTemplate): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
console.log("is summary", !!summary);
|
||||
return (
|
||||
<Fragment>
|
||||
<section style={{ textAlign: "left" }}>
|
||||
|
@ -51,7 +51,6 @@ export function useClipboardPermissions(): ToggleHandler {
|
||||
// .call("toggleHeaderListener", false)
|
||||
// .then((r) => setEnabled(r.newValue));
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
}
|
||||
return;
|
||||
|
@ -36,7 +36,6 @@ export function useSettings(): [
|
||||
function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
|
||||
const newValue = { ...parsed, [k]: v };
|
||||
const json = JSON.stringify(newValue);
|
||||
console.log(json);
|
||||
update(json);
|
||||
}
|
||||
return [parsed, updateField];
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useBackendContext } from "../context/backend.js";
|
||||
|
||||
type Result = {
|
||||
value: boolean | undefined;
|
||||
toggle: () => Promise<void>;
|
||||
};
|
||||
|
||||
export function useWalletDevMode(): Result {
|
||||
const [enabled, setEnabled] = useState<undefined | boolean>(undefined);
|
||||
const api = useBackendContext();
|
||||
// const { pushAlertOnError } = useAlertContext();
|
||||
|
||||
async function handleOpen(): Promise<void> {
|
||||
const nextValue = !enabled;
|
||||
await api.wallet.call(WalletApiOperation.SetDevMode, {
|
||||
devModeEnabled: nextValue,
|
||||
});
|
||||
setEnabled(nextValue);
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function getValue(): Promise<void> {
|
||||
const res = await api.wallet.call(WalletApiOperation.GetVersion, {});
|
||||
setEnabled(res.devMode);
|
||||
}
|
||||
getValue();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
value: enabled,
|
||||
toggle: handleOpen,
|
||||
};
|
||||
}
|
@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import { CoreApiResponse, NotificationType } from "@gnu-taler/taler-util";
|
||||
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
|
||||
import {
|
||||
WalletConfig,
|
||||
WalletConfigParameter,
|
||||
WalletOperations,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import { BackgroundOperations } from "../wxApi.js";
|
||||
import {
|
||||
ExtensionOperations,
|
||||
@ -87,12 +91,35 @@ export interface WalletWebExVersion {
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
type F = WalletConfig["features"];
|
||||
type kf = keyof F;
|
||||
type WebexWalletConfig = {
|
||||
[P in keyof F as `wallet${Capitalize<P>}`]: F[P];
|
||||
};
|
||||
|
||||
export interface Settings extends WebexWalletConfig {
|
||||
injectTalerSupport: boolean;
|
||||
advanceMode: boolean;
|
||||
backup: boolean;
|
||||
langSelector: boolean;
|
||||
showJsonOnError: boolean;
|
||||
extendedAccountTypes: boolean;
|
||||
//WORKAROUND
|
||||
//Able to delete tx in dev mode
|
||||
//FIXME: remove this when DD37 is implemented
|
||||
deleteActiveTransactions: boolean;
|
||||
}
|
||||
|
||||
export const defaultSettings: Settings = {
|
||||
injectTalerSupport: false,
|
||||
advanceMode: false,
|
||||
backup: false,
|
||||
langSelector: false,
|
||||
deleteActiveTransactions: false,
|
||||
showJsonOnError: false,
|
||||
extendedAccountTypes: false,
|
||||
walletAllowHttp: false,
|
||||
walletBatchWithdrawal: false,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,7 @@ function isFirefox(): boolean {
|
||||
}
|
||||
|
||||
function addPermissionsListener(callback: (p: Permissions) => void): void {
|
||||
console.log("addPermissionListener is not supported for Firefox");
|
||||
throw Error("addPermissionListener is not supported for Firefox");
|
||||
}
|
||||
|
||||
function getPermissionsApi(): CrossBrowserPermissionsApi {
|
||||
|
@ -20,6 +20,10 @@
|
||||
* @author sebasjm
|
||||
*/
|
||||
|
||||
import {
|
||||
TranslationProvider,
|
||||
useTranslationContext,
|
||||
} from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { createHashHistory } from "history";
|
||||
import { ComponentChildren, Fragment, h, VNode } from "preact";
|
||||
import { route, Route, Router } from "preact-router";
|
||||
@ -27,29 +31,22 @@ import { useEffect, useState } from "preact/hooks";
|
||||
import PendingTransactions from "../components/PendingTransactions.js";
|
||||
import { PopupBox } from "../components/styled/index.js";
|
||||
import { AlertProvider } from "../context/alert.js";
|
||||
import { DevContextProvider } from "../context/devContext.js";
|
||||
import { IoCProviderForRuntime } from "../context/iocContext.js";
|
||||
import {
|
||||
TranslationProvider,
|
||||
useTranslationContext,
|
||||
} from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { useTalerActionURL } from "../hooks/useTalerActionURL.js";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";
|
||||
import { platform } from "../platform/foreground.js";
|
||||
import { BackupPage } from "../wallet/BackupPage.js";
|
||||
import { ProviderDetailPage } from "../wallet/ProviderDetailPage.js";
|
||||
import { BalancePage } from "./BalancePage.js";
|
||||
import { TalerActionFound } from "./TalerActionFound.js";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
|
||||
export function Application(): VNode {
|
||||
return (
|
||||
<TranslationProvider source={strings}>
|
||||
<DevContextProvider>
|
||||
<IoCProviderForRuntime>
|
||||
<ApplicationView />
|
||||
</IoCProviderForRuntime>
|
||||
</DevContextProvider>
|
||||
<IoCProviderForRuntime>
|
||||
<ApplicationView />
|
||||
</IoCProviderForRuntime>
|
||||
</TranslationProvider>
|
||||
);
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import { setupPlatform } from "./platform/foreground.js";
|
||||
import devAPI from "./platform/dev.js";
|
||||
import { Application } from "./popup/Application.js";
|
||||
|
||||
console.log("Wallet setup for Dev API");
|
||||
setupPlatform(devAPI);
|
||||
|
||||
function main(): void {
|
||||
|
@ -32,10 +32,8 @@ import { Application } from "./popup/Application.js";
|
||||
//switching in runtime
|
||||
const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
|
||||
if (isFirefox) {
|
||||
console.log("Wallet setup for Firefox API");
|
||||
setupPlatform(firefoxAPI);
|
||||
} else {
|
||||
console.log("Wallet setup for Chrome API");
|
||||
setupPlatform(chromeAPI);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ import { strings } from "./i18n/strings.js";
|
||||
// When doing tests we want the requestAnimationFrame to be as fast as possible.
|
||||
// without this option the RAF will timeout after 100ms making the tests slower
|
||||
options.requestAnimationFrame = (fn: () => void) => {
|
||||
// console.log("RAF called")
|
||||
return fn();
|
||||
};
|
||||
|
||||
|
@ -20,15 +20,21 @@
|
||||
* @author sebasjm
|
||||
*/
|
||||
|
||||
import { TranslatedString } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
TalerUriAction,
|
||||
TranslatedString,
|
||||
parseTalerUri,
|
||||
} from "@gnu-taler/taler-util";
|
||||
TranslationProvider,
|
||||
useTranslationContext,
|
||||
} from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { createHashHistory } from "history";
|
||||
import { ComponentChildren, Fragment, h, VNode } from "preact";
|
||||
import { route, Route, Router } from "preact-router";
|
||||
import { ComponentChildren, Fragment, VNode, h } from "preact";
|
||||
import { Route, Router, route } from "preact-router";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import {
|
||||
Pages,
|
||||
WalletNavBar,
|
||||
WalletNavBarOptions,
|
||||
getPathnameForTalerURI,
|
||||
} from "../NavigationBar.js";
|
||||
import { AlertView, CurrentAlerts } from "../components/CurrentAlerts.js";
|
||||
import { LogoHeader } from "../components/LogoHeader.js";
|
||||
import PendingTransactions from "../components/PendingTransactions.js";
|
||||
@ -39,12 +45,7 @@ import {
|
||||
WalletBox,
|
||||
} from "../components/styled/index.js";
|
||||
import { AlertProvider } from "../context/alert.js";
|
||||
import { DevContextProvider } from "../context/devContext.js";
|
||||
import { IoCProviderForRuntime } from "../context/iocContext.js";
|
||||
import {
|
||||
TranslationProvider,
|
||||
useTranslationContext,
|
||||
} from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { DepositPage as DepositPageCTA } from "../cta/Deposit/index.js";
|
||||
import { InvoiceCreatePage } from "../cta/InvoiceCreate/index.js";
|
||||
import { InvoicePayPage } from "../cta/InvoicePay/index.js";
|
||||
@ -59,12 +60,7 @@ import {
|
||||
WithdrawPageFromParams,
|
||||
WithdrawPageFromURI,
|
||||
} from "../cta/Withdraw/index.js";
|
||||
import {
|
||||
Pages,
|
||||
WalletNavBar,
|
||||
WalletNavBarOptions,
|
||||
getPathnameForTalerURI,
|
||||
} from "../NavigationBar.js";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
import { platform } from "../platform/foreground.js";
|
||||
import CloseIcon from "../svg/close_24px.svg";
|
||||
import { AddBackupProviderPage } from "./AddBackupProvider/index.js";
|
||||
@ -80,7 +76,6 @@ import { QrReaderPage } from "./QrReader.js";
|
||||
import { SettingsPage } from "./Settings.js";
|
||||
import { TransactionPage } from "./Transaction.js";
|
||||
import { WelcomePage } from "./Welcome.js";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
|
||||
export function Application(): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
@ -91,433 +86,421 @@ export function Application(): VNode {
|
||||
}
|
||||
return (
|
||||
<TranslationProvider source={strings}>
|
||||
<DevContextProvider>
|
||||
<IoCProviderForRuntime>
|
||||
<Router history={hash_history}>
|
||||
<Route
|
||||
path={Pages.welcome}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<WelcomePage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<IoCProviderForRuntime>
|
||||
<Router history={hash_history}>
|
||||
<Route
|
||||
path={Pages.welcome}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<WelcomePage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.qr}
|
||||
component={() => (
|
||||
<WalletTemplate goToTransaction={redirectToTxInfo}>
|
||||
<QrReaderPage
|
||||
onDetected={(talerActionUrl: string) => {
|
||||
platform.openWalletURIFromPopup(talerActionUrl);
|
||||
}}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.qr}
|
||||
component={() => (
|
||||
<WalletTemplate goToTransaction={redirectToTxInfo}>
|
||||
<QrReaderPage
|
||||
onDetected={(talerActionUrl: string) => {
|
||||
platform.openWalletURIFromPopup(talerActionUrl);
|
||||
}}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.settings}
|
||||
component={() => (
|
||||
<WalletTemplate goToTransaction={redirectToTxInfo}>
|
||||
<SettingsPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.notifications}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<NotificationsPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
{/**
|
||||
* SETTINGS
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.settingsExchangeAdd.pattern}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<ExchangeAddPage onBack={() => redirectTo(Pages.balance)} />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.settings}
|
||||
component={() => (
|
||||
<WalletTemplate goToTransaction={redirectToTxInfo}>
|
||||
<SettingsPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.notifications}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<NotificationsPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
{/**
|
||||
* SETTINGS
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.settingsExchangeAdd.pattern}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<ExchangeAddPage onBack={() => redirectTo(Pages.balance)} />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.balanceHistory.pattern}
|
||||
component={({ currency }: { currency?: string }) => (
|
||||
<WalletTemplate
|
||||
path="balance"
|
||||
goToTransaction={redirectToTxInfo}
|
||||
>
|
||||
<HistoryPage
|
||||
currency={currency}
|
||||
goToWalletDeposit={(currency: string) =>
|
||||
redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
|
||||
}
|
||||
goToWalletManualWithdraw={(currency?: string) =>
|
||||
redirectTo(
|
||||
Pages.receiveCash({
|
||||
amount: !currency ? undefined : `${currency}:0`,
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.sendCash.pattern}
|
||||
component={({ amount }: { amount?: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DestinationSelectionPage
|
||||
type="send"
|
||||
amount={amount}
|
||||
goToWalletBankDeposit={(amount: string) =>
|
||||
redirectTo(Pages.balanceDeposit({ amount }))
|
||||
}
|
||||
goToWalletWalletSend={(amount: string) =>
|
||||
redirectTo(Pages.ctaTransferCreate({ amount }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.receiveCash.pattern}
|
||||
component={({ amount }: { amount?: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DestinationSelectionPage
|
||||
type="get"
|
||||
amount={amount}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.ctaWithdrawManual({ amount }))
|
||||
}
|
||||
goToWalletWalletInvoice={(amount?: string) =>
|
||||
redirectTo(Pages.ctaInvoiceCreate({ amount }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.balanceHistory.pattern}
|
||||
component={({ currency }: { currency?: string }) => (
|
||||
<WalletTemplate path="balance" goToTransaction={redirectToTxInfo}>
|
||||
<HistoryPage
|
||||
currency={currency}
|
||||
goToWalletDeposit={(currency: string) =>
|
||||
redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
|
||||
}
|
||||
goToWalletManualWithdraw={(currency?: string) =>
|
||||
redirectTo(
|
||||
Pages.receiveCash({
|
||||
amount: !currency ? undefined : `${currency}:0`,
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.sendCash.pattern}
|
||||
component={({ amount }: { amount?: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DestinationSelectionPage
|
||||
type="send"
|
||||
amount={amount}
|
||||
goToWalletBankDeposit={(amount: string) =>
|
||||
redirectTo(Pages.balanceDeposit({ amount }))
|
||||
}
|
||||
goToWalletWalletSend={(amount: string) =>
|
||||
redirectTo(Pages.ctaTransferCreate({ amount }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.receiveCash.pattern}
|
||||
component={({ amount }: { amount?: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DestinationSelectionPage
|
||||
type="get"
|
||||
amount={amount}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.ctaWithdrawManual({ amount }))
|
||||
}
|
||||
goToWalletWalletInvoice={(amount?: string) =>
|
||||
redirectTo(Pages.ctaInvoiceCreate({ amount }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.balanceTransaction.pattern}
|
||||
component={({ tid }: { tid: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<TransactionPage
|
||||
tid={tid}
|
||||
goToWalletHistory={(currency?: string) =>
|
||||
redirectTo(Pages.balanceHistory({ currency }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.balanceTransaction.pattern}
|
||||
component={({ tid }: { tid: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<TransactionPage
|
||||
tid={tid}
|
||||
goToWalletHistory={(currency?: string) =>
|
||||
redirectTo(Pages.balanceHistory({ currency }))
|
||||
}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.balanceDeposit.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DepositPage
|
||||
amount={amount}
|
||||
onCancel={(currency: string) => {
|
||||
redirectTo(Pages.balanceHistory({ currency }));
|
||||
}}
|
||||
onSuccess={(currency: string) => {
|
||||
redirectTo(Pages.balanceHistory({ currency }));
|
||||
}}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.balanceDeposit.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<WalletTemplate path="balance">
|
||||
<DepositPage
|
||||
amount={amount}
|
||||
onCancel={(currency: string) => {
|
||||
redirectTo(Pages.balanceHistory({ currency }));
|
||||
}}
|
||||
onSuccess={(currency: string) => {
|
||||
redirectTo(Pages.balanceHistory({ currency }));
|
||||
}}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Pages.backup}
|
||||
component={() => (
|
||||
<WalletTemplate
|
||||
path="backup"
|
||||
goToTransaction={redirectToTxInfo}
|
||||
>
|
||||
<BackupPage
|
||||
onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.backupProviderDetail.pattern}
|
||||
component={({ pid }: { pid: string }) => (
|
||||
<WalletTemplate>
|
||||
<ProviderDetailPage
|
||||
pid={pid}
|
||||
onPayProvider={(uri: string) =>
|
||||
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
|
||||
}
|
||||
onWithdraw={(amount: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
onBack={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.backupProviderAdd}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<AddBackupProviderPage
|
||||
onPaymentRequired={(uri: string) =>
|
||||
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
|
||||
}
|
||||
onComplete={(pid: string) =>
|
||||
redirectTo(Pages.backupProviderDetail({ pid }))
|
||||
}
|
||||
onBack={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.backup}
|
||||
component={() => (
|
||||
<WalletTemplate path="backup" goToTransaction={redirectToTxInfo}>
|
||||
<BackupPage
|
||||
onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.backupProviderDetail.pattern}
|
||||
component={({ pid }: { pid: string }) => (
|
||||
<WalletTemplate>
|
||||
<ProviderDetailPage
|
||||
pid={pid}
|
||||
onPayProvider={(uri: string) =>
|
||||
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
|
||||
}
|
||||
onWithdraw={(amount: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
onBack={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.backupProviderAdd}
|
||||
component={() => (
|
||||
<WalletTemplate>
|
||||
<AddBackupProviderPage
|
||||
onPaymentRequired={(uri: string) =>
|
||||
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
|
||||
}
|
||||
onComplete={(pid: string) =>
|
||||
redirectTo(Pages.backupProviderDetail({ pid }))
|
||||
}
|
||||
onBack={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/**
|
||||
* DEV
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.dev}
|
||||
component={() => (
|
||||
<WalletTemplate path="dev" goToTransaction={redirectToTxInfo}>
|
||||
<DeveloperPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
{/**
|
||||
* DEV
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.dev}
|
||||
component={() => (
|
||||
<WalletTemplate path="dev" goToTransaction={redirectToTxInfo}>
|
||||
<DeveloperPage />
|
||||
</WalletTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/**
|
||||
* CALL TO ACTION
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.defaultCta.pattern}
|
||||
component={({ uri }: { uri: string }) => {
|
||||
const path = getPathnameForTalerURI(uri);
|
||||
if (!path) {
|
||||
return (
|
||||
<CallToActionTemplate title={i18n.str`Taler URI handler`}>
|
||||
<AlertView
|
||||
alert={{
|
||||
type: "warning",
|
||||
message: i18n.str`Could not found a handler for the Taler URI`,
|
||||
description: i18n.str`The uri read in the path parameter is not valid: "${uri}"`,
|
||||
}}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
);
|
||||
}
|
||||
return <Redirect to={path} />;
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaPay}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash payment`}>
|
||||
<PaymentPage
|
||||
talerPayUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaPayTemplate}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash payment`}>
|
||||
<PaymentTemplatePage
|
||||
talerTemplateUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaRefund}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash refund`}>
|
||||
<RefundPage
|
||||
talerRefundUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTips}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash tip`}>
|
||||
<TipPage
|
||||
talerTipUri={decodeURIComponent(talerUri)}
|
||||
onCancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdraw}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
||||
<WithdrawPageFromURI
|
||||
talerWithdrawUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdrawManual.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
||||
<WithdrawPageFromParams
|
||||
amount={amount}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaDeposit}
|
||||
component={({
|
||||
amount,
|
||||
talerUri,
|
||||
}: {
|
||||
amount: string;
|
||||
talerUri: string;
|
||||
}) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash deposit`}>
|
||||
<DepositPageCTA
|
||||
amountStr={amount}
|
||||
talerDepositUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoiceCreate.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash invoice`}>
|
||||
<InvoiceCreatePage
|
||||
amount={amount}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferCreate.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash transfer`}>
|
||||
<TransferCreatePage
|
||||
amount={amount}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoicePay}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash invoice`}>
|
||||
<InvoicePayPage
|
||||
talerPayPullUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferPickup}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash transfer`}>
|
||||
<TransferPickupPage
|
||||
talerPayPushUri={decodeURIComponent(talerUri)}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaRecovery}
|
||||
component={({
|
||||
talerRecoveryUri,
|
||||
}: {
|
||||
talerRecoveryUri: string;
|
||||
}) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash recovery`}>
|
||||
<RecoveryPage
|
||||
talerRecoveryUri={decodeURIComponent(talerRecoveryUri)}
|
||||
onCancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
{/**
|
||||
* CALL TO ACTION
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.defaultCta.pattern}
|
||||
component={({ uri }: { uri: string }) => {
|
||||
const path = getPathnameForTalerURI(uri);
|
||||
if (!path) {
|
||||
return (
|
||||
<CallToActionTemplate title={i18n.str`Taler URI handler`}>
|
||||
<AlertView
|
||||
alert={{
|
||||
type: "warning",
|
||||
message: i18n.str`Could not found a handler for the Taler URI`,
|
||||
description: i18n.str`The uri read in the path parameter is not valid: "${uri}"`,
|
||||
}}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
);
|
||||
}
|
||||
return <Redirect to={path} />;
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaPay}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash payment`}>
|
||||
<PaymentPage
|
||||
talerPayUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaPayTemplate}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash payment`}>
|
||||
<PaymentTemplatePage
|
||||
talerTemplateUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaRefund}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash refund`}>
|
||||
<RefundPage
|
||||
talerRefundUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTips}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash tip`}>
|
||||
<TipPage
|
||||
talerTipUri={decodeURIComponent(talerUri)}
|
||||
onCancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdraw}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
||||
<WithdrawPageFromURI
|
||||
talerWithdrawUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdrawManual.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
||||
<WithdrawPageFromParams
|
||||
amount={amount}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaDeposit}
|
||||
component={({
|
||||
amount,
|
||||
talerUri,
|
||||
}: {
|
||||
amount: string;
|
||||
talerUri: string;
|
||||
}) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash deposit`}>
|
||||
<DepositPageCTA
|
||||
amountStr={amount}
|
||||
talerDepositUri={decodeURIComponent(talerUri)}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoiceCreate.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash invoice`}>
|
||||
<InvoiceCreatePage
|
||||
amount={amount}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferCreate.pattern}
|
||||
component={({ amount }: { amount: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash transfer`}>
|
||||
<TransferCreatePage
|
||||
amount={amount}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoicePay}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash invoice`}>
|
||||
<InvoicePayPage
|
||||
talerPayPullUri={decodeURIComponent(talerUri)}
|
||||
goToWalletManualWithdraw={(amount?: string) =>
|
||||
redirectTo(Pages.receiveCash({ amount }))
|
||||
}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferPickup}
|
||||
component={({ talerUri }: { talerUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash transfer`}>
|
||||
<TransferPickupPage
|
||||
talerPayPushUri={decodeURIComponent(talerUri)}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid: string) =>
|
||||
redirectTo(Pages.balanceTransaction({ tid }))
|
||||
}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaRecovery}
|
||||
component={({ talerRecoveryUri }: { talerRecoveryUri: string }) => (
|
||||
<CallToActionTemplate title={i18n.str`Digital cash recovery`}>
|
||||
<RecoveryPage
|
||||
talerRecoveryUri={decodeURIComponent(talerRecoveryUri)}
|
||||
onCancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={() => redirectTo(Pages.backup)}
|
||||
/>
|
||||
</CallToActionTemplate>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/**
|
||||
* NOT FOUND
|
||||
* all redirects should be at the end
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.balance}
|
||||
component={() => <Redirect to={Pages.balanceHistory({})} />}
|
||||
/>
|
||||
{/**
|
||||
* NOT FOUND
|
||||
* all redirects should be at the end
|
||||
*/}
|
||||
<Route
|
||||
path={Pages.balance}
|
||||
component={() => <Redirect to={Pages.balanceHistory({})} />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
default
|
||||
component={() => <Redirect to={Pages.balanceHistory({})} />}
|
||||
/>
|
||||
</Router>
|
||||
</IoCProviderForRuntime>
|
||||
</DevContextProvider>
|
||||
<Route
|
||||
default
|
||||
component={() => <Redirect to={Pages.balanceHistory({})} />}
|
||||
/>
|
||||
</Router>
|
||||
</IoCProviderForRuntime>
|
||||
</TranslationProvider>
|
||||
);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { styled } from "@linaria/react";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { AmountField } from "../../components/AmountField.js";
|
||||
import { JustInDevMode } from "../../components/JustInDevMode.js";
|
||||
import { EnabledBySettings } from "../../components/EnabledBySettings.js";
|
||||
import { SelectList } from "../../components/SelectList.js";
|
||||
import {
|
||||
Input,
|
||||
@ -300,11 +300,11 @@ export function ReadySendView({
|
||||
required
|
||||
handler={amountHandler}
|
||||
/>
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="advanceMode">
|
||||
<Button onClick={sendAll.onClick}>
|
||||
<i18n.Translate>Send all</i18n.Translate>
|
||||
</Button>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
</Grid>
|
||||
|
||||
<Grid container spacing={1} columns={1}>
|
||||
|
@ -23,10 +23,10 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||
import { useState } from "preact/hooks";
|
||||
import { alertFromError, useAlertContext } from "../../context/alert.js";
|
||||
import { useBackendContext } from "../../context/backend.js";
|
||||
import { useDevContext } from "../../context/devContext.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
import { AccountByType, Props, State } from "./index.js";
|
||||
import { useSettings } from "../../hooks/useSettings.js";
|
||||
|
||||
export function useComponentState({
|
||||
currency,
|
||||
@ -39,12 +39,11 @@ export function useComponentState({
|
||||
const hook = useAsyncAsHook(() =>
|
||||
api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
|
||||
);
|
||||
const { devMode } = useDevContext();
|
||||
const accountType: Record<string, string> = {
|
||||
iban: "IBAN",
|
||||
// "x-taler-bank": "Taler Bank",
|
||||
};
|
||||
if (devMode) {
|
||||
const [settings] = useSettings();
|
||||
if (settings.extendedAccountTypes) {
|
||||
accountType["bitcoin"] = "Bitcoin";
|
||||
accountType["x-taler-bank"] = "Taler Bank";
|
||||
}
|
||||
|
@ -47,27 +47,27 @@ const version = {
|
||||
|
||||
export const AllOff = tests.createExample(TestedComponent, {
|
||||
deviceName: "this-is-the-device-name",
|
||||
devModeToggle: { value: false, button: {} },
|
||||
advanceToggle: { value: false, button: {} },
|
||||
autoOpenToggle: { value: false, button: {} },
|
||||
clipboardToggle: { value: false, button: {} },
|
||||
langToggle: { value: false, button: {} },
|
||||
setDeviceName: () => Promise.resolve(),
|
||||
...version,
|
||||
});
|
||||
|
||||
export const OneChecked = tests.createExample(TestedComponent, {
|
||||
deviceName: "this-is-the-device-name",
|
||||
devModeToggle: { value: false, button: {} },
|
||||
advanceToggle: { value: false, button: {} },
|
||||
autoOpenToggle: { value: false, button: {} },
|
||||
clipboardToggle: { value: false, button: {} },
|
||||
langToggle: { value: false, button: {} },
|
||||
setDeviceName: () => Promise.resolve(),
|
||||
...version,
|
||||
});
|
||||
|
||||
export const WithOneExchange = tests.createExample(TestedComponent, {
|
||||
deviceName: "this-is-the-device-name",
|
||||
devModeToggle: { value: false, button: {} },
|
||||
advanceToggle: { value: false, button: {} },
|
||||
autoOpenToggle: { value: false, button: {} },
|
||||
clipboardToggle: { value: false, button: {} },
|
||||
langToggle: { value: false, button: {} },
|
||||
setDeviceName: () => Promise.resolve(),
|
||||
knownExchanges: [
|
||||
{
|
||||
@ -89,9 +89,9 @@ export const WithExchangeInDifferentState = tests.createExample(
|
||||
TestedComponent,
|
||||
{
|
||||
deviceName: "this-is-the-device-name",
|
||||
devModeToggle: { value: false, button: {} },
|
||||
advanceToggle: { value: false, button: {} },
|
||||
autoOpenToggle: { value: false, button: {} },
|
||||
clipboardToggle: { value: false, button: {} },
|
||||
langToggle: { value: false, button: {} },
|
||||
setDeviceName: () => Promise.resolve(),
|
||||
knownExchanges: [
|
||||
{
|
||||
|
@ -17,12 +17,15 @@
|
||||
import {
|
||||
ExchangeListItem,
|
||||
ExchangeTosStatus,
|
||||
TranslatedString,
|
||||
WalletCoreVersion,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { Fragment, VNode, h } from "preact";
|
||||
import { Pages } from "../NavigationBar.js";
|
||||
import { Checkbox } from "../components/Checkbox.js";
|
||||
import { JustInDevMode } from "../components/JustInDevMode.js";
|
||||
import { EnabledBySettings } from "../components/EnabledBySettings.js";
|
||||
import { Part } from "../components/Part.js";
|
||||
import { SelectList } from "../components/SelectList.js";
|
||||
import {
|
||||
@ -35,25 +38,20 @@ import {
|
||||
} from "../components/styled/index.js";
|
||||
import { useAlertContext } from "../context/alert.js";
|
||||
import { useBackendContext } from "../context/backend.js";
|
||||
import { useDevContext } from "../context/devContext.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||
import { useSettings } from "../hooks/useSettings.js";
|
||||
import { useBackupDeviceName } from "../hooks/useBackupDeviceName.js";
|
||||
import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js";
|
||||
import { useSettings } from "../hooks/useSettings.js";
|
||||
import { ToggleHandler } from "../mui/handlers.js";
|
||||
import { Pages } from "../NavigationBar.js";
|
||||
import { platform } from "../platform/foreground.js";
|
||||
import { Settings } from "../platform/api.js";
|
||||
|
||||
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
|
||||
|
||||
export function SettingsPage(): VNode {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const clipboardToggle = useClipboardPermissions();
|
||||
const { devMode, toggle } = useDevContext();
|
||||
const { safely } = useAlertContext();
|
||||
const { name, update } = useBackupDeviceName();
|
||||
const { pushAlertOnError } = useAlertContext();
|
||||
const webex = platform.getWalletWebExVersion();
|
||||
const api = useBackendContext();
|
||||
|
||||
@ -76,16 +74,24 @@ export function SettingsPage(): VNode {
|
||||
value: settings.injectTalerSupport,
|
||||
button: {
|
||||
onClick: safely("update support injection", async () => {
|
||||
console.log("click", settings.injectTalerSupport);
|
||||
updateSettings("injectTalerSupport", !settings.injectTalerSupport);
|
||||
}),
|
||||
},
|
||||
}}
|
||||
clipboardToggle={clipboardToggle}
|
||||
devModeToggle={{
|
||||
value: devMode,
|
||||
advanceToggle={{
|
||||
value: settings.advanceMode,
|
||||
button: {
|
||||
onClick: pushAlertOnError(toggle),
|
||||
onClick: safely("update advance mode", async () => {
|
||||
updateSettings("advanceMode", !settings.advanceMode);
|
||||
}),
|
||||
},
|
||||
}}
|
||||
langToggle={{
|
||||
value: settings.langSelector,
|
||||
button: {
|
||||
onClick: safely("update lang selector", async () => {
|
||||
updateSettings("langSelector", !settings.langSelector);
|
||||
}),
|
||||
},
|
||||
}}
|
||||
webexVersion={{
|
||||
@ -101,8 +107,8 @@ export interface ViewProps {
|
||||
deviceName: string;
|
||||
setDeviceName: (s: string) => Promise<void>;
|
||||
autoOpenToggle: ToggleHandler;
|
||||
clipboardToggle: ToggleHandler;
|
||||
devModeToggle: ToggleHandler;
|
||||
advanceToggle: ToggleHandler;
|
||||
langToggle: ToggleHandler;
|
||||
knownExchanges: Array<ExchangeListItem>;
|
||||
coreVersion: WalletCoreVersion | undefined;
|
||||
webexVersion: {
|
||||
@ -114,8 +120,8 @@ export interface ViewProps {
|
||||
export function SettingsView({
|
||||
knownExchanges,
|
||||
autoOpenToggle,
|
||||
clipboardToggle,
|
||||
devModeToggle,
|
||||
advanceToggle,
|
||||
langToggle,
|
||||
coreVersion,
|
||||
webexVersion,
|
||||
}: ViewProps): VNode {
|
||||
@ -204,45 +210,19 @@ export function SettingsView({
|
||||
</LinkPrimary>
|
||||
</div>
|
||||
|
||||
<JustInDevMode>
|
||||
<SubTitle>
|
||||
<i18n.Translate>Display</i18n.Translate>
|
||||
</SubTitle>
|
||||
<Input>
|
||||
<SelectList
|
||||
label={<i18n.Translate>Current Language</i18n.Translate>}
|
||||
list={supportedLang}
|
||||
name="lang"
|
||||
value={lang}
|
||||
onChange={(v) => changeLanguage(v)}
|
||||
/>
|
||||
</Input>
|
||||
</JustInDevMode>
|
||||
<SubTitle>
|
||||
<i18n.Translate>Version</i18n.Translate>
|
||||
</SubTitle>
|
||||
{coreVersion && (
|
||||
<Part
|
||||
title={i18n.str`Wallet Core`}
|
||||
text={
|
||||
<span>
|
||||
{coreVersion.version}{" "}
|
||||
<JustInDevMode>{coreVersion.hash}</JustInDevMode>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Part
|
||||
title={i18n.str`Web Extension`}
|
||||
text={
|
||||
<span>
|
||||
{webexVersion.version}{" "}
|
||||
<JustInDevMode>{webexVersion.hash}</JustInDevMode>
|
||||
<EnabledBySettings name="advanceMode">
|
||||
{webexVersion.hash}
|
||||
</EnabledBySettings>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
{coreVersion && (
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="advanceMode">
|
||||
<Part
|
||||
title={i18n.str`Exchange compatibility`}
|
||||
text={<span>{coreVersion.exchange}</span>}
|
||||
@ -255,18 +235,42 @@ export function SettingsView({
|
||||
title={i18n.str`Bank compatibility`}
|
||||
text={<span>{coreVersion.bank}</span>}
|
||||
/>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
)}
|
||||
<SubTitle>
|
||||
<i18n.Translate>Troubleshooting</i18n.Translate>
|
||||
<i18n.Translate>Advance mode</i18n.Translate>
|
||||
</SubTitle>
|
||||
<Checkbox
|
||||
label={i18n.str`Developer mode`}
|
||||
label={i18n.str`Enable advance mode`}
|
||||
name="devMode"
|
||||
description={i18n.str`More options and information useful for debugging`}
|
||||
enabled={devModeToggle.value!}
|
||||
onToggle={devModeToggle.button.onClick!}
|
||||
description={i18n.str`Show more information and options in the UI`}
|
||||
enabled={advanceToggle.value!}
|
||||
onToggle={advanceToggle.button.onClick!}
|
||||
/>
|
||||
<EnabledBySettings name="advanceMode">
|
||||
<AdvanceSettings />
|
||||
</EnabledBySettings>
|
||||
<Checkbox
|
||||
label={i18n.str`Lang selector`}
|
||||
name="langSelector"
|
||||
description={i18n.str`Allows to manually change the language of the UI. Otherwise it will be automatically selected by your browser configuration.`}
|
||||
enabled={langToggle.value!}
|
||||
onToggle={langToggle.button.onClick!}
|
||||
/>
|
||||
<EnabledBySettings name="langSelector">
|
||||
<SubTitle>
|
||||
<i18n.Translate>Display</i18n.Translate>
|
||||
</SubTitle>
|
||||
<Input>
|
||||
<SelectList
|
||||
label={<i18n.Translate>Current Language</i18n.Translate>}
|
||||
list={supportedLang}
|
||||
name="lang"
|
||||
value={lang}
|
||||
onChange={(v) => changeLanguage(v)}
|
||||
/>
|
||||
</Input>
|
||||
</EnabledBySettings>
|
||||
<SubTitle>
|
||||
<i18n.Translate>Navigator</i18n.Translate>
|
||||
</SubTitle>
|
||||
@ -283,6 +287,77 @@ export function SettingsView({
|
||||
enabled={autoOpenToggle.value!}
|
||||
onToggle={autoOpenToggle.button.onClick!}
|
||||
/>
|
||||
<SubTitle>
|
||||
<i18n.Translate>Version</i18n.Translate>
|
||||
</SubTitle>
|
||||
{coreVersion && (
|
||||
<Part
|
||||
title={i18n.str`Wallet Core`}
|
||||
text={
|
||||
<span>
|
||||
{coreVersion.version}{" "}
|
||||
<EnabledBySettings name="advanceMode">
|
||||
{coreVersion.hash}
|
||||
</EnabledBySettings>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
type Info = { label: TranslatedString; description: TranslatedString };
|
||||
type Options = {
|
||||
[k in keyof Settings]?: Info;
|
||||
};
|
||||
function AdvanceSettings(): VNode {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const { i18n } = useTranslationContext();
|
||||
const o: Options = {
|
||||
backup: {
|
||||
label: i18n.str`Show backup feature`,
|
||||
description: i18n.str`Backup integration still in beta.`,
|
||||
},
|
||||
deleteActiveTransactions: {
|
||||
label: i18n.str`Show delete active transaction`,
|
||||
description: i18n.str`Deleting active transaction is not safe and you may loose your coins.`,
|
||||
},
|
||||
extendedAccountTypes: {
|
||||
label: i18n.str`Show more account types on deposit`,
|
||||
description: i18n.str`Extends the UI to more payment target types.`,
|
||||
},
|
||||
showJsonOnError: {
|
||||
label: i18n.str`Show JSON on error`,
|
||||
description: i18n.str`Print more information about the error. Useful for debugging.`,
|
||||
},
|
||||
walletAllowHttp: {
|
||||
label: i18n.str`Allow HTTP connections`,
|
||||
description: i18n.str`Using HTTP connection may be faster but unsafe (wallet restart required)`,
|
||||
},
|
||||
walletBatchWithdrawal: {
|
||||
label: i18n.str`Allow batch withdrawals`,
|
||||
description: i18n.str`Using the batch withdrawal API allows faster withdrawals (wallet restart required)`,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<Fragment>
|
||||
<section>
|
||||
{Object.entries(o).map(([name, { label, description }]) => {
|
||||
const settingsName = name as keyof Settings;
|
||||
return (
|
||||
<Checkbox
|
||||
label={label}
|
||||
name={name}
|
||||
description={description}
|
||||
enabled={settings[settingsName]}
|
||||
onToggle={async () => {
|
||||
updateSettings(settingsName, !settings[settingsName]);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -34,6 +34,7 @@ import {
|
||||
WithdrawalType,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { styled } from "@linaria/react";
|
||||
import { differenceInSeconds, isPast } from "date-fns";
|
||||
import { ComponentChildren, Fragment, h, VNode } from "preact";
|
||||
@ -43,7 +44,6 @@ import { Amount } from "../components/Amount.js";
|
||||
import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js";
|
||||
import { CopyButton } from "../components/CopyButton.js";
|
||||
import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js";
|
||||
import { JustInDevMode } from "../components/JustInDevMode.js";
|
||||
import { Loading } from "../components/Loading.js";
|
||||
import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js";
|
||||
import { QR } from "../components/QR.js";
|
||||
@ -61,12 +61,12 @@ import {
|
||||
import { Time } from "../components/Time.js";
|
||||
import { alertFromError, useAlertContext } from "../context/alert.js";
|
||||
import { useBackendContext } from "../context/backend.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
|
||||
import { Button } from "../mui/Button.js";
|
||||
import { SafeHandler } from "../mui/handlers.js";
|
||||
import { Pages } from "../NavigationBar.js";
|
||||
import { assertUnreachable } from "../utils/index.js";
|
||||
import { EnabledBySettings } from "../components/EnabledBySettings.js";
|
||||
|
||||
interface Props {
|
||||
tid: string;
|
||||
@ -359,18 +359,15 @@ function TransactionTemplate({
|
||||
<i18n.Translate>Cancel</i18n.Translate>
|
||||
</Button>
|
||||
) : (
|
||||
//WORKAROUND
|
||||
//Able to delete tx in dev mode
|
||||
//FIXME: remove this when DD37 is implemented
|
||||
<JustInDevMode>
|
||||
<EnabledBySettings name="deleteActiveTransactions">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={doCheckBeforeForget as SafeHandler<void>}
|
||||
>
|
||||
<i18n.Translate>Forget</i18n.Translate>
|
||||
<i18n.Translate>Delete</i18n.Translate>
|
||||
</Button>
|
||||
</JustInDevMode>
|
||||
</EnabledBySettings>
|
||||
)
|
||||
) : (
|
||||
<Button
|
||||
@ -378,7 +375,7 @@ function TransactionTemplate({
|
||||
color="error"
|
||||
onClick={doCheckBeforeForget as SafeHandler<void>}
|
||||
>
|
||||
<i18n.Translate>Forget</i18n.Translate>
|
||||
<i18n.Translate>Delete</i18n.Translate>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -27,7 +27,6 @@ import { setupPlatform } from "./platform/foreground.js";
|
||||
import devAPI from "./platform/dev.js";
|
||||
import { Application } from "./wallet/Application.js";
|
||||
|
||||
console.log("Wallet setup for Dev API");
|
||||
setupPlatform(devAPI);
|
||||
|
||||
function main(): void {
|
||||
|
@ -33,10 +33,8 @@ const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
|
||||
//FIXME: create different entry point for any platform instead of
|
||||
//switching in runtime
|
||||
if (isFirefox) {
|
||||
console.log("Wallet setup for Firefox API");
|
||||
setupPlatform(firefoxAPI);
|
||||
} else {
|
||||
console.log("Wallet setup for Chrome API");
|
||||
setupPlatform(chromeAPI);
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ class WalletApiClientImpl implements WalletCoreApiClient {
|
||||
};
|
||||
response = await platform.sendMessageToBackground(message);
|
||||
} catch (e) {
|
||||
console.log("Error calling backend");
|
||||
logger.error("Error calling backend", e);
|
||||
throw new Error(`Error contacting backend: ${e}`);
|
||||
}
|
||||
if (response.type === "error") {
|
||||
|
@ -319,12 +319,19 @@ async function reinitWallet(): Promise<void> {
|
||||
timer = new SetTimeoutTimerAPI();
|
||||
}
|
||||
|
||||
const settings = await platform.getSettingsFromStorage();
|
||||
logger.info("Setting up wallet");
|
||||
const wallet = await Wallet.create(
|
||||
currentDatabase,
|
||||
httpLib,
|
||||
timer,
|
||||
cryptoWorker,
|
||||
{
|
||||
features: {
|
||||
allowHttp: settings.walletAllowHttp,
|
||||
batchWithdrawal: settings.walletBatchWithdrawal,
|
||||
},
|
||||
},
|
||||
);
|
||||
try {
|
||||
await wallet.handleCoreApiRequest("initWallet", "native-init", {});
|
||||
|
Loading…
Reference in New Issue
Block a user