From 55f868d5e83f577cd20ad6f33e0cf8776b4d0f45 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 14 Feb 2023 13:02:59 +0100 Subject: wallet-core: allow inclusion of refreshes in transactions list --- packages/taler-util/src/transactions-types.ts | 10 ++-- packages/taler-wallet-cli/src/index.ts | 4 +- .../src/operations/transactions.ts | 53 ++++++++++++++++++++++ .../src/components/TransactionItem.tsx | 2 +- .../src/wallet/History.stories.tsx | 3 ++ .../src/wallet/Transaction.stories.tsx | 3 ++ .../src/wallet/Transaction.tsx | 2 +- 7 files changed, 69 insertions(+), 8 deletions(-) (limited to 'packages') diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index 946f9dda9..98e333ad3 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -68,6 +68,11 @@ export interface TransactionsRequest { * if present, results will be limited to transactions related to the given search string */ search?: string; + + /** + * If true, include all refreshes in the transactions list. + */ + includeRefreshes?: boolean; } export interface TransactionsResponse { @@ -514,11 +519,6 @@ export interface TransactionTip extends TransactionCommon { export interface TransactionRefresh extends TransactionCommon { type: TransactionType.Refresh; - /** - * Exchange that the coins are refreshed with - */ - exchangeBaseUrl: string; - refreshReason: RefreshReason; /** diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index 7e942ede7..228395991 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -381,7 +381,8 @@ walletCli const transactionsCli = walletCli .subcommand("transactions", "transactions", { help: "Manage transactions." }) .maybeOption("currency", ["--currency"], clk.STRING) - .maybeOption("search", ["--search"], clk.STRING); + .maybeOption("search", ["--search"], clk.STRING) + .flag("includeRefreshes", ["--include-refreshes"]); // Default action transactionsCli.action(async (args) => { @@ -391,6 +392,7 @@ transactionsCli.action(async (args) => { { currency: args.transactions.currency, search: args.transactions.search, + includeRefreshes: args.transactions.includeRefreshes, }, ); console.log(JSON.stringify(pending, undefined, 2)); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 8aae2332c..9ebd87cc0 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -56,6 +56,8 @@ import { PeerPullPaymentIncomingStatus, TransactionStatus, WithdrawalGroupStatus, + RefreshGroupRecord, + RefreshOperationStatus, } from "../db.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { checkDbInvariant } from "../util/invariants.js"; @@ -580,6 +582,45 @@ function buildTransactionForManualWithdraw( }; } +function buildTransactionForRefresh( + refreshGroupRecord: RefreshGroupRecord, + ort?: OperationRetryRecord, +): Transaction { + let extendedStatus: ExtendedStatus; + switch (refreshGroupRecord.operationStatus) { + case RefreshOperationStatus.Finished: + case RefreshOperationStatus.FinishedWithError: + extendedStatus = ExtendedStatus.Done; + break; + default: + extendedStatus = ExtendedStatus.Pending; + } + return { + type: TransactionType.Refresh, + refreshReason: refreshGroupRecord.reason, + amountEffective: Amounts.stringify( + Amounts.zeroOfCurrency(refreshGroupRecord.currency), + ), + amountRaw: Amounts.stringify( + Amounts.zeroOfCurrency(refreshGroupRecord.currency), + ), + extendedStatus: + refreshGroupRecord.operationStatus === RefreshOperationStatus.Finished || + refreshGroupRecord.operationStatus === + RefreshOperationStatus.FinishedWithError + ? ExtendedStatus.Done + : ExtendedStatus.Pending, + pending: extendedStatus == ExtendedStatus.Pending, + timestamp: refreshGroupRecord.timestampCreated, + transactionId: makeTransactionId( + TransactionType.Refresh, + refreshGroupRecord.refreshGroupId, + ), + frozen: false, + ...(ort?.lastError ? { error: ort.lastError } : {}), + }; +} + function buildTransactionForDeposit( dg: DepositGroupRecord, ort?: OperationRetryRecord, @@ -880,6 +921,7 @@ export async function getTransactions( x.tips, x.tombstones, x.withdrawalGroups, + x.refreshGroups, ]) .runReadOnly(async (tx) => { tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => { @@ -916,6 +958,17 @@ export async function getTransactions( transactions.push(buildTransactionForPullPaymentDebit(pi)); }); + if (transactionsRequest?.includeRefreshes) { + tx.refreshGroups.iter().forEachAsync(async (rg) => { + if (shouldSkipCurrency(transactionsRequest, rg.currency)) { + return; + } + const opId = RetryTags.forRefresh(rg); + const ort = await tx.operationRetries.get(opId); + transactions.push(buildTransactionForRefresh(rg, ort)); + }); + } + tx.withdrawalGroups.iter().forEachAsync(async (wsr) => { if ( shouldSkipCurrency( diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx index 71d7edaf0..934a0fe52 100644 --- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -110,7 +110,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode { id={tx.transactionId} amount={tx.amountEffective} debitCreditIndicator={"credit"} - title={new URL(tx.exchangeBaseUrl).hostname} + title={"Refresh"} timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)} iconPath={"R"} // pending={tx.pending} diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx index d89027e5f..2ebaf8536 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -21,6 +21,7 @@ import { PaymentStatus, + RefreshReason, ScopeType, TalerProtocolTimestamp, TransactionCommon, @@ -90,6 +91,7 @@ const exampleData = { totalRefundRaw: "USD:0", proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, + refundQueryActive: false, } as TransactionPayment, deposit: { ...commonTransaction(), @@ -101,6 +103,7 @@ const exampleData = { ...commonTransaction(), type: TransactionType.Refresh, exchangeBaseUrl: "http://exchange.taler", + refreshReason: RefreshReason.PayMerchant, } as TransactionRefresh, tip: { ...commonTransaction(), diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx index a2aa9f26f..c29cd99cc 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx @@ -23,6 +23,7 @@ import { AbsoluteTime, ExtendedStatus, PaymentStatus, + RefreshReason, TalerProtocolTimestamp, TransactionCommon, TransactionDeposit, @@ -111,6 +112,7 @@ const exampleData = { totalRefundRaw: "KUDOS:0", proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, + refundQueryActive: false, } as TransactionPayment, deposit: { ...commonTransaction, @@ -125,6 +127,7 @@ const exampleData = { ...commonTransaction, type: TransactionType.Refresh, exchangeBaseUrl: "http://exchange.taler", + refreshReason: RefreshReason.Manual, } as TransactionRefresh, tip: { ...commonTransaction, diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index d91f621db..fc94f977a 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -729,7 +729,7 @@ export function TransactionView({ total={effective} kind="negative" > - {transaction.exchangeBaseUrl} + {"Refresh"}