diff options
author | Sebastian <sebasjm@gmail.com> | 2023-04-19 12:42:47 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-04-19 12:42:47 -0300 |
commit | d483a3f5574355ed9c43eb6ddea59e5734323cf0 (patch) | |
tree | 2f4e3830512b50808b396991791c3c8877e5676e | |
parent | 7330f0daf907133876baf8831c44ec34cec326e5 (diff) | |
download | wallet-core-d483a3f5574355ed9c43eb6ddea59e5734323cf0.tar.xz |
fix #7704
48 files changed, 958 insertions, 1009 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 7a2442e2d..5979f14b4 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -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; } diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index fadf84d5c..cc7c119b9 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -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(); } diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 5dc13433a..04bfa1359 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -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; } } diff --git a/packages/taler-wallet-core/src/dev-experiments.ts b/packages/taler-wallet-core/src/dev-experiments.ts index 3e6194ccd..113e9bede 100644 --- a/packages/taler-wallet-core/src/dev-experiments.ts +++ b/packages/taler-wallet-core/src/dev-experiments.ts @@ -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; diff --git a/packages/taler-wallet-core/src/host-common.ts b/packages/taler-wallet-core/src/host-common.ts index 7651e5a12..21e7f1157 100644 --- a/packages/taler-wallet-core/src/host-common.ts +++ b/packages/taler-wallet-core/src/host-common.ts @@ -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; } /** diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts index 097b5856a..bf5c14f99 100644 --- a/packages/taler-wallet-core/src/host-impl.node.ts +++ b/packages/taler-wallet-core/src/host-impl.node.ts @@ -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); diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts index 337914292..db7a1fa5c 100644 --- a/packages/taler-wallet-core/src/host-impl.qtart.ts +++ b/packages/taler-wallet-core/src/host-impl.qtart.ts @@ -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); diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts index 7b21d8f91..8dd06fe2b 100644 --- a/packages/taler-wallet-core/src/index.ts +++ b/packages/taler-wallet-core/src/index.ts @@ -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"; diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts index 8434c3b8f..e59781471 100644 --- a/packages/taler-wallet-core/src/internal-wallet-state.ts +++ b/packages/taler-wallet-core/src/internal-wallet-state.ts @@ -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<{ diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index d1dbf5f53..700b875d3 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -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); } diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index d9051b32f..b5e02e64d 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -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; } diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 496641fe7..2419d32a2 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -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, + ); } } diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts b/packages/taler-wallet-core/src/operations/pay-peer.ts index ebf521079..33659afe0 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer.ts @@ -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); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 70f0579c0..3122c9685 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -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); diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 643737e93..23c3e6713 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -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 = { diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index 176d636fc..3b986d72b 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -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), diff --git a/packages/taler-wallet-core/src/util/debugFlags.ts b/packages/taler-wallet-core/src/util/debugFlags.ts deleted file mode 100644 index cea249d27..000000000 --- a/packages/taler-wallet-core/src/util/debugFlags.ts +++ /dev/null @@ -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, -}; diff --git a/packages/taler-wallet-core/src/util/denominations.ts b/packages/taler-wallet-core/src/util/denominations.ts index fb766e96a..5cc57bdab 100644 --- a/packages/taler-wallet-core/src/util/denominations.ts +++ b/packages/taler-wallet-core/src/util/denominations.ts @@ -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( diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index 2ac649f59..94348f095 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -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; + }; +} diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index d6d64b5c9..45b33ce45 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -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 { + checkPeerPullPaymentInitiation, + checkPeerPushDebit, confirmPeerPullDebit, confirmPeerPushCredit, - preparePeerPullDebit, - preparePeerPushCredit, initiatePeerPullPayment, initiatePeerPushPayment, - checkPeerPullPaymentInitiation, - checkPeerPushDebit, + 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; +} diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index d6b22ba28..806646a02 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -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" }} diff --git a/packages/taler-wallet-webextension/src/background.dev.ts b/packages/taler-wallet-webextension/src/background.dev.ts index 9ed0e1b8f..96cf63409 100644 --- a/packages/taler-wallet-webextension/src/background.dev.ts +++ b/packages/taler-wallet-webextension/src/background.dev.ts @@ -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() { diff --git a/packages/taler-wallet-webextension/src/background.ts b/packages/taler-wallet-webextension/src/background.ts index 0d5a186d2..7df66eff8 100644 --- a/packages/taler-wallet-webextension/src/background.ts +++ b/packages/taler-wallet-webextension/src/background.ts @@ -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); } diff --git a/packages/taler-wallet-webextension/src/components/JustInDevMode.tsx b/packages/taler-wallet-webextension/src/components/EnabledBySettings.tsx index a29bea791..6f666d301 100644 --- a/packages/taler-wallet-webextension/src/components/JustInDevMode.tsx +++ b/packages/taler-wallet-webextension/src/components/EnabledBySettings.tsx @@ -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>; } diff --git a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx index f8203f38a..2979c28e5 100644 --- a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx +++ b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx @@ -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> diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts index 391932574..fdca78ee5 100644 --- a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts +++ b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts @@ -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; diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts deleted file mode 100644 index e2ad2914b..000000000 --- a/packages/taler-wallet-webextension/src/context/devContext.ts +++ /dev/null @@ -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 }); -}; diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts b/packages/taler-wallet-webextension/src/cta/Payment/state.ts index b3d2e6e81..5cd4686ce 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts @@ -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); diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx index 42e51014f..bd265750f 100644 --- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx @@ -30,7 +30,6 @@ export function ReadyView({ }: State.FillTemplate): VNode { const { i18n } = useTranslationContext(); - console.log("is summary", !!summary); return ( <Fragment> <section style={{ textAlign: "left" }}> diff --git a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts index 3255c90e5..35b7148cc 100644 --- a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts @@ -51,7 +51,6 @@ export function useClipboardPermissions(): ToggleHandler { // .call("toggleHeaderListener", false) // .then((r) => setEnabled(r.newValue)); // } catch (e) { - // console.log(e); // } } return; diff --git a/packages/taler-wallet-webextension/src/hooks/useSettings.ts b/packages/taler-wallet-webextension/src/hooks/useSettings.ts index 040fee424..1fa2b0539 100644 --- a/packages/taler-wallet-webextension/src/hooks/useSettings.ts +++ b/packages/taler-wallet-webextension/src/hooks/useSettings.ts @@ -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]; diff --git a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts deleted file mode 100644 index db7effe96..000000000 --- a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts +++ /dev/null @@ -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, - }; -} diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index f87500c4f..f8fa0a848 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -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, }; /** diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts index 01848e1ab..9f666e7ae 100644 --- a/packages/taler-wallet-webextension/src/platform/firefox.ts +++ b/packages/taler-wallet-webextension/src/platform/firefox.ts @@ -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 { diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx index c03360486..e0cb840aa 100644 --- a/packages/taler-wallet-webextension/src/popup/Application.tsx +++ b/packages/taler-wallet-webextension/src/popup/Application.tsx @@ -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> ); } diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx index 6b42e5809..f0bc81399 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx @@ -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 { diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 22aba6c9d..08915ea96 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -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); } diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts index d85d992b1..1d2538703 100644 --- a/packages/taler-wallet-webextension/src/test-utils.ts +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -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(); }; diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx index ff8cf0314..5c75b734b 100644 --- a/packages/taler-wallet-webextension/src/wallet/Application.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx @@ -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> ); } diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx index 7f239f33d..8416e17e9 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx @@ -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}> diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts index 1f8ab4883..641b47cd7 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts @@ -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"; } diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx index 53bc577d4..89d92b82c 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx @@ -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: [ { diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index ae3a6e688..62a6c55ff 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -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> ); diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 1ef0f0b79..9bcae8997 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -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> diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx index c2906373c..60a5970e4 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx @@ -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 { diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index 84822b8e6..1bd42796b 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -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); } diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index d15528699..ce1dac14f 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -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") { diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index bf91e8521..8acc41247 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -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", {}); |