diff options
Diffstat (limited to 'packages')
13 files changed, 257 insertions, 141 deletions
diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index bce1e6b39..b3b197891 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -277,6 +277,8 @@ interface WithdrawalDetailsForTalerBankIntegrationApi { * Is the reserve ready for withdrawal? */ reserveIsReady: boolean; + + exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[]; } /** diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index e92b0d449..4cc11d9eb 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -577,11 +577,11 @@ export interface ExchangeDetailsRecord { * Information about ToS acceptance from the user. */ tosAccepted: - | { - etag: string; - timestamp: DbPreciseTimestamp; - } - | undefined; + | { + etag: string; + timestamp: DbPreciseTimestamp; + } + | undefined; wireInfo: WireInfo; @@ -1334,9 +1334,9 @@ export enum ConfigRecordKey { */ export type ConfigRecord = | { - key: ConfigRecordKey.WalletBackupState; - value: WalletBackupConfState; - } + key: ConfigRecordKey.WalletBackupState; + value: WalletBackupConfState; + } | { key: ConfigRecordKey.CurrencyDefaultsApplied; value: boolean } | { key: ConfigRecordKey.DevMode; value: boolean }; @@ -1378,6 +1378,10 @@ export interface WgInfoBankIntegrated { * a Taler-integrated bank. */ bankInfo: ReserveBankInfo; + /** + * Info about withdrawal accounts, possibly including currency conversion. + */ + exchangeCreditAccounts?: WithdrawalExchangeAccountDetails[]; } export interface WgInfoBankManual { @@ -1599,15 +1603,15 @@ export enum BackupProviderStateTag { export type BackupProviderState = | { - tag: BackupProviderStateTag.Provisional; - } + tag: BackupProviderStateTag.Provisional; + } | { - tag: BackupProviderStateTag.Ready; - nextBackupTimestamp: DbPreciseTimestamp; - } + tag: BackupProviderStateTag.Ready; + nextBackupTimestamp: DbPreciseTimestamp; + } | { - tag: BackupProviderStateTag.Retrying; - }; + tag: BackupProviderStateTag.Retrying; + }; export interface BackupProviderTerms { supportedProtocolVersion: string; @@ -3065,7 +3069,7 @@ export async function openStoredBackupsDatabase( idbFactory, TALER_WALLET_STORED_BACKUPS_DB_NAME, 1, - () => {}, + () => { }, onStoredBackupsDbUpgradeNeeded, ); @@ -3088,7 +3092,7 @@ export async function openTalerDatabase( idbFactory, TALER_WALLET_META_DB_NAME, 1, - () => {}, + () => { }, onMetaDbUpgradeNeeded, ); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 0ff552916..ee8c90407 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -540,7 +540,7 @@ function buildTransactionForPeerPullCredit( const silentWithdrawalErrorForInvoice = wsrOrt?.lastError && wsrOrt.lastError.code === - TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE && + TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE && Object.values(wsrOrt.lastError.errorsPerCoin ?? {}).every((e) => { return ( e.code === TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR && @@ -570,10 +570,10 @@ function buildTransactionForPeerPullCredit( kycUrl: pullCredit.kycUrl, ...(wsrOrt?.lastError ? { - error: silentWithdrawalErrorForInvoice - ? undefined - : wsrOrt.lastError, - } + error: silentWithdrawalErrorForInvoice + ? undefined + : wsrOrt.lastError, + } : {}), }; } @@ -674,6 +674,7 @@ function buildTransactionForBankIntegratedWithdraw( withdrawalDetails: { type: WithdrawalType.TalerBankIntegrationApi, confirmed: wgRecord.wgInfo.bankInfo.timestampBankConfirmed ? true : false, + exchangeCreditAccountDetails: wgRecord.wgInfo.exchangeCreditAccounts, reservePub: wgRecord.reservePub, bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl, reserveIsReady: diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 9c798e813..868ac3adc 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -866,10 +866,10 @@ async function handleKycRequired( amlStatus === AmlStatus.normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc : amlStatus === AmlStatus.pending - ? WithdrawalGroupStatus.PendingAml - : amlStatus === AmlStatus.fronzen - ? WithdrawalGroupStatus.SuspendedAml - : assertUnreachable(amlStatus); + ? WithdrawalGroupStatus.PendingAml + : amlStatus === AmlStatus.fronzen + ? WithdrawalGroupStatus.SuspendedAml + : assertUnreachable(amlStatus); notificationKycUrl = kycUrl; @@ -1205,8 +1205,7 @@ export async function updateWithdrawalDenoms( denom.verificationStatus === DenominationVerificationStatus.Unverified ) { logger.trace( - `Validating denomination (${current + 1}/${ - denominations.length + `Validating denomination (${current + 1}/${denominations.length }) signature of ${denom.denomPubHash}`, ); let valid = false; @@ -1852,7 +1851,7 @@ export async function getExchangeWithdrawalInfo( ) { logger.warn( `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + - `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, + `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, ); } } @@ -2524,11 +2523,22 @@ export async function acceptWithdrawalFromUri( withdrawInfo.wireTypes, ); + const { exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl( + ws, + selectedExchange, + ); + + const withdrawalAccountList = await fetchWithdrawalAccountInfo(ws, { + exchangeDetails, + instructedAmount: withdrawInfo.amount, + }); + const withdrawalGroup = await internalCreateWithdrawalGroup(ws, { amount: withdrawInfo.amount, exchangeBaseUrl: req.selectedExchange, wgInfo: { withdrawalType: WithdrawalRecordType.BankIntegrated, + exchangeCreditAccounts: withdrawalAccountList, bankInfo: { exchangePaytoUri, talerWithdrawUri: req.talerWithdrawUri, diff --git a/packages/taler-wallet-webextension/build.mjs b/packages/taler-wallet-webextension/build.mjs index d972ee610..e1fd5c0e1 100755 --- a/packages/taler-wallet-webextension/build.mjs +++ b/packages/taler-wallet-webextension/build.mjs @@ -17,6 +17,7 @@ import { build } from "@gnu-taler/web-util/build"; import linaria from "@linaria/esbuild"; +import { shakerPlugin } from "@linaria/shaker"; await build({ type: "production", @@ -42,7 +43,7 @@ await build({ "@linaria", ], }, - sourceMap: true, + // sourceMap: true, }); }, }); diff --git a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx index dd8abc509..a28c34987 100644 --- a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx +++ b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx @@ -18,51 +18,40 @@ import { AmountJson, Amounts, parsePaytoUri, - PaytoUri, segwitMinAmount, stringifyPaytoUri, TranslatedString, + WithdrawalExchangeAccountDetails } from "@gnu-taler/taler-util"; -import { Fragment, h, VNode } from "preact"; -import { useEffect, useRef, useState } from "preact/hooks"; import { - useAsyncAsHook, - useTranslationContext, + useTranslationContext } from "@gnu-taler/web-util/browser"; +import { Fragment, h, VNode } from "preact"; +import { useEffect, useRef, useState } from "preact/hooks"; import { CopiedIcon, CopyIcon } from "../svg/index.js"; import { Amount } from "./Amount.js"; import { ButtonBox, TooltipLeft, WarningBox } from "./styled/index.js"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { useBackendContext } from "../context/backend.js"; export interface BankDetailsProps { - exchangeBaseUrl: string; subject: string; amount: AmountJson; + accounts: WithdrawalExchangeAccountDetails[], } export function BankDetailsByPaytoType({ subject, - exchangeBaseUrl, amount, + accounts, }: BankDetailsProps): VNode { const { i18n } = useTranslationContext(); - const api = useBackendContext(); - const hook = useAsyncAsHook(async () => { - const details = await api.wallet.call( - WalletApiOperation.GetExchangeDetailedInfo, - { - exchangeBaseUrl, - }, - ); - return { details }; - }); - - if (!hook || hook.hasError) return <Fragment />; + if (!accounts.length) { + return <div>exchange doesn't have accounts to wire </div> + } + const [index, setIndex] = useState(0) + const fisrtAccount = accounts[index]; - const firstPayto = hook.response.details.exchange.paytoUris[0]; - const payto = parsePaytoUri(firstPayto); + const payto = parsePaytoUri(fisrtAccount.paytoUri); if (!payto) return <Fragment />; payto.params["amount"] = Amounts.stringify(amount); @@ -149,6 +138,7 @@ export function BankDetailsByPaytoType({ const receiver = payto.params["receiver"] || undefined; return ( <section> + <div style={{ textAlign: "left", diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts index 3dc2b7f3d..5fb3b1d80 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts @@ -17,6 +17,7 @@ import { AmountJson, AmountString, + CurrencySpecification, ExchangeListItem, WithdrawalExchangeAccountDetails, } from "@gnu-taler/taler-util"; @@ -92,7 +93,14 @@ export namespace State { doWithdrawal: ButtonHandler; doSelectExchange: ButtonHandler; - accounts: WithdrawalExchangeAccountDetails[]; + + chooseCurrencies: string[]; + selectedCurrency: string; + changeCurrency: (s: string) => void; + conversionInfo: { + spec: CurrencySpecification, + amount: AmountJson, + } | undefined; ageRestriction?: SelectFieldHandler; diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index 46d221766..c10572d1a 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -297,7 +297,7 @@ function exchangeSelectionState( return selectedExchange; } - return () => { + return (): State.Success | State.LoadingUriError | State.Loading => { const { i18n } = useTranslationContext(); const { pushAlertOnError } = useAlertContext(); const [ageRestricted, setAgeRestricted] = useState(0); @@ -306,6 +306,7 @@ function exchangeSelectionState( currentExchange.tosStatus == ExchangeTosStatus.Pending || currentExchange.tosStatus == ExchangeTosStatus.Proposed; + const [selectedCurrency, setSelectedCurrency] = useState<string>(chosenAmount.currency) /** * With the exchange and amount, ask the wallet the information * about the withdrawal @@ -324,7 +325,7 @@ function exchangeSelectionState( raw: Amounts.parseOrThrow(info.amountRaw), effective: Amounts.parseOrThrow(info.amountEffective), }; - + return { amount: withdrawAmount, ageRestrictionOptions: info.ageRestrictionOptions, @@ -336,7 +337,7 @@ function exchangeSelectionState( undefined, ); const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false); - + async function doWithdrawAndCheckError(): Promise<void> { try { setDoingWithdraw(true); @@ -401,13 +402,26 @@ function exchangeSelectionState( } : undefined; + const altCurrencies = amountHook.response.accounts.filter(a => !!a.currencySpecification).map(a => a.currencySpecification!.name) + const chooseCurrencies = altCurrencies.length === 0 ? [] : [toBeReceived.currency, ...altCurrencies] + const convAccount = amountHook.response.accounts.find(c => { + return c.currencySpecification && c.currencySpecification.name === selectedCurrency + }) + const conversionInfo = !convAccount ? undefined : ({ + spec: convAccount.currencySpecification!, + amount: Amounts.parseOrThrow(convAccount.transferAmount!) + }) + return { status: "success", error: undefined, doSelectExchange: selectedExchange.doSelect, currentExchange, toBeReceived, - accounts: amountHook.response.accounts, + chooseCurrencies, + selectedCurrency, + changeCurrency: (s) => { setSelectedCurrency(s) }, + conversionInfo, withdrawalFee, chosenAmount, talerWithdrawUri, @@ -417,7 +431,6 @@ function exchangeSelectionState( doingWithdraw || tosNeedToBeAccepted ? undefined : pushAlertOnError(doWithdrawAndCheckError), - error: withdrawError, }, onTosUpdate, cancel, diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx index df70a5c95..a3127fafc 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ExchangeListItem } from "@gnu-taler/taler-util"; +import { CurrencySpecification, ExchangeListItem } from "@gnu-taler/taler-util"; import * as tests from "@gnu-taler/web-util/testing"; import { nullFunction } from "../../mui/handlers.js"; // import { TermsState } from "../../utils/index.js"; @@ -64,7 +64,7 @@ export const TermsOfServiceNotYetLoaded = tests.createExample(SuccessView, { fraction: 0, value: 1, }, - accounts: [], + chooseCurrencies: [], }); export const WithSomeFee = tests.createExample(SuccessView, { @@ -91,7 +91,7 @@ export const WithSomeFee = tests.createExample(SuccessView, { value: 1, }, doSelectExchange: {}, - accounts: [], + chooseCurrencies: [], }); export const WithoutFee = tests.createExample(SuccessView, { @@ -118,7 +118,7 @@ export const WithoutFee = tests.createExample(SuccessView, { fraction: 0, value: 2, }, - accounts: [], + chooseCurrencies: [], }); export const EditExchangeUntouched = tests.createExample(SuccessView, { @@ -145,7 +145,7 @@ export const EditExchangeUntouched = tests.createExample(SuccessView, { fraction: 0, value: 2, }, - accounts: [], + chooseCurrencies: [], }); export const EditExchangeModified = tests.createExample(SuccessView, { @@ -172,7 +172,7 @@ export const EditExchangeModified = tests.createExample(SuccessView, { fraction: 0, value: 2, }, - accounts: [], + chooseCurrencies: [], }); export const WithAgeRestriction = tests.createExample(SuccessView, { @@ -200,31 +200,110 @@ export const WithAgeRestriction = tests.createExample(SuccessView, { fraction: 0, value: 2, }, - accounts: [], + chooseCurrencies: [], }); -export const WithAlternateCurrencies = tests.createExample(SuccessView, { +export const WithAlternateCurrenciesNETZBON = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { - currency: "USD", + currency: "NETZBON", value: 2, fraction: 10000000, }, - accounts: [], + chooseCurrencies: ["NETZBON", "EUR"], + selectedCurrency: "NETZBON", doWithdrawal: { onClick: nullFunction }, currentExchange: { - exchangeBaseUrl: "https://exchange.demo.taler.net", + exchangeBaseUrl: "https://exchange.netzbon.ch", tos: {}, } as Partial<ExchangeListItem> as any, withdrawalFee: { - currency: "USD", + currency: "NETZBON", fraction: 10000000, value: 1, }, doSelectExchange: {}, toBeReceived: { - currency: "USD", + currency: "NETZBON", + fraction: 0, + value: 1, + }, +}); + +export const WithAlternateCurrenciesEURO = tests.createExample(SuccessView, { + error: undefined, + status: "success", + chosenAmount: { + currency: "NETZBON", + value: 2, + fraction: 10000000, + }, + chooseCurrencies: ["NETZBON", "EUR"], + selectedCurrency: "EUR", + changeCurrency: () => { }, + conversionInfo: { + spec: { + name: "EUR" + } as CurrencySpecification, + amount: { + currency: "EUR", + fraction: 10000000, + value: 1, + } + }, + doWithdrawal: { onClick: nullFunction }, + currentExchange: { + exchangeBaseUrl: "https://exchange.netzbon.ch", + tos: {}, + } as Partial<ExchangeListItem> as any, + withdrawalFee: { + currency: "NETZBON", + fraction: 10000000, + value: 1, + }, + doSelectExchange: {}, + toBeReceived: { + currency: "NETZBON", + fraction: 0, + value: 1, + }, +}); + +export const WithAlternateCurrenciesEURO11 = tests.createExample(SuccessView, { + error: undefined, + status: "success", + chosenAmount: { + currency: "NETZBON", + value: 2, + fraction: 10000000, + }, + chooseCurrencies: ["NETZBON", "EUR"], + selectedCurrency: "EUR", + changeCurrency: () => { }, + conversionInfo: { + spec: { + name: "EUR" + } as CurrencySpecification, + amount: { + currency: "EUR", + fraction: 10000000, + value: 2, + } + }, + doWithdrawal: { onClick: nullFunction }, + currentExchange: { + exchangeBaseUrl: "https://exchange.netzbon.ch", + tos: {}, + } as Partial<ExchangeListItem> as any, + withdrawalFee: { + currency: "NETZBON", + fraction: 10000000, + value: 1, + }, + doSelectExchange: {}, + toBeReceived: { + currency: "NETZBON", fraction: 0, value: 1, }, diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx index aa8b58707..d732e60e2 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx @@ -39,8 +39,7 @@ export function SuccessView(state: State.Success): VNode { const { i18n } = useTranslationContext(); const currentTosVersionIsAccepted = state.currentExchange.tosStatus === ExchangeTosStatus.Accepted; - const altCurrencies = state.accounts.filter(a => !!a.currencySpecification).map(a => a.currencySpecification!.name) - return ( + return ( <Fragment> <section style={{ textAlign: "left" }}> <Part @@ -67,25 +66,27 @@ export function SuccessView(state: State.Success): VNode { kind="neutral" big /> - <p> - This exchange allows alternative currency - </p> - <p> - <Button - variant="outlined" - > - EUR - </Button> - <Button - variant="outlined" - > - ARS - </Button> - </p> + {state.chooseCurrencies.length > 0 ? + <Fragment> + <p> + {state.chooseCurrencies.map(currency => { + return <Button variant={currency === state.selectedCurrency ? "contained" : "outlined"} + onClick={async () => { + state.changeCurrency(currency) + }} + > + {currency} + </Button> + })} + </p> + </Fragment> + : <Fragment />} + <Part title={i18n.str`Details`} text={ <WithdrawDetails + conversion={state.conversionInfo?.amount} amount={getAmountWithFee( state.toBeReceived, state.chosenAmount, diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx index 81250d5a4..2fcf580ed 100644 --- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx @@ -39,7 +39,7 @@ export const TalerBank = tests.createExample(TestedComponent, { value: 10, fraction: 0, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); export const IBAN = tests.createExample(TestedComponent, { @@ -52,7 +52,7 @@ export const IBAN = tests.createExample(TestedComponent, { value: 10, fraction: 0, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); export const WithReceiverName = tests.createExample(TestedComponent, { @@ -65,7 +65,7 @@ export const WithReceiverName = tests.createExample(TestedComponent, { value: 10, fraction: 0, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); export const Bitcoin = tests.createExample(TestedComponent, { @@ -78,7 +78,7 @@ export const Bitcoin = tests.createExample(TestedComponent, { value: 0, fraction: 14000000, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); export const BitcoinRegTest = tests.createExample(TestedComponent, { @@ -91,7 +91,7 @@ export const BitcoinRegTest = tests.createExample(TestedComponent, { value: 0, fraction: 14000000, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); export const BitcoinTest = tests.createExample(TestedComponent, { reservePub: "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00", @@ -103,5 +103,5 @@ export const BitcoinTest = tests.createExample(TestedComponent, { value: 0, fraction: 14000000, }, - exchangeBaseUrl: "https://exchange.demo.taler.net", + accounts: [] }); diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx index f489b805b..144413541 100644 --- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx @@ -13,7 +13,7 @@ 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 { AmountJson, PaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util"; +import { AmountJson, PaytoUri, WithdrawalExchangeAccountDetails, stringifyPaytoUri } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { Amount } from "../components/Amount.js"; import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js"; @@ -26,7 +26,7 @@ import { Button } from "../mui/Button.js"; export interface Props { reservePub: string; paytoURI: PaytoUri | undefined; - exchangeBaseUrl: string; + accounts: WithdrawalExchangeAccountDetails[]; amount: AmountJson; onCancel: () => Promise<void>; } @@ -35,7 +35,7 @@ export function ReserveCreated({ reservePub, paytoURI, onCancel, - exchangeBaseUrl, + accounts, amount, }: Props): VNode { const { i18n } = useTranslationContext(); @@ -47,17 +47,6 @@ export function ReserveCreated({ /> ); } - function TransferDetails(): VNode { - if (!paytoURI) return <Fragment />; - return ( - <BankDetailsByPaytoType - amount={amount} - exchangeBaseUrl={exchangeBaseUrl} - subject={reservePub} - /> - ); - } - return ( <Fragment> <section> @@ -71,7 +60,11 @@ export function ReserveCreated({ </i18n.Translate> </p> </section> - <TransferDetails /> + <BankDetailsByPaytoType + amount={amount} + accounts={accounts} + subject={reservePub} + /> <section> <p> <i18n.Translate> diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 4383dcc9a..e7ab65722 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -19,7 +19,6 @@ import { AmountJson, Amounts, AmountString, - Location, MerchantInfo, NotificationType, OrderShortInfo, @@ -28,7 +27,6 @@ import { stringifyPaytoUri, TalerErrorCode, TalerPreciseTimestamp, - TalerProtocolTimestamp, Transaction, TransactionAction, TransactionDeposit, @@ -39,7 +37,7 @@ import { TransactionType, TransactionWithdrawal, TranslatedString, - WithdrawalType, + WithdrawalType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; @@ -47,13 +45,12 @@ import { styled } from "@linaria/react"; import { isPast } from "date-fns"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import emptyImg from "../../static/img/empty.png"; import { Amount } from "../components/Amount.js"; import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js"; import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js"; import { EnabledBySettings } from "../components/EnabledBySettings.js"; import { Loading } from "../components/Loading.js"; -import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js"; +import { Kind, Part, PartPayto } from "../components/Part.js"; import { QR } from "../components/QR.js"; import { ShowFullContractTermPopup } from "../components/ShowFullContractTermPopup.js"; import { @@ -61,13 +58,11 @@ import { ErrorBox, InfoBox, Link, - ListOfProducts, Overlay, - Row, SmallLightText, SubTitle, SvgIcon, - WarningBox, + WarningBox } from "../components/styled/index.js"; import { Time } from "../components/Time.js"; import { alertFromError, useAlertContext } from "../context/alert.js"; @@ -77,12 +72,8 @@ import { useSettings } from "../hooks/useSettings.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 refreshIcon from "../svg/refresh_24px.inline.svg"; -import refreshIcon1 from "../svg/refresh_outlined_24px.inline.svg"; -import refreshIcon2 from "../svg/refresh_rounded_24px.inline.svg"; -import refreshIcon3 from "../svg/refresh_sharp_24px.inline.svg"; -import refreshIcon4 from "../svg/refresh_two_tone_24px.inline.svg"; +import { assertUnreachable } from "../utils/index.js"; interface Props { tid: string; @@ -444,6 +435,8 @@ export function TransactionView({ transaction.type === TransactionType.Withdrawal || transaction.type === TransactionType.InternalWithdrawal ) { + const conversion = transaction.withdrawalDetails.type === WithdrawalType.ManualTransfer ? + transaction.withdrawalDetails.exchangeCreditAccountDetails ?? [] : [] return ( <TransactionTemplate transaction={transaction} @@ -472,7 +465,7 @@ export function TransactionView({ //manual withdrawal <BankDetailsByPaytoType amount={raw} - exchangeBaseUrl={transaction.exchangeBaseUrl} + accounts={transaction.withdrawalDetails.exchangeCreditAccountDetails ?? []} subject={transaction.withdrawalDetails.reservePub} /> ) : ( @@ -973,12 +966,12 @@ export function TransactionView({ kind="neutral" /> {transaction.talerUri && ( - <Part - title={i18n.str`URI`} - text={<ShowQrWithCopy text={transaction.talerUri} />} - kind="neutral" - /> - )} + <Part + title={i18n.str`URI`} + text={<ShowQrWithCopy text={transaction.talerUri} />} + kind="neutral" + /> + )} <Part title={i18n.str`Details`} text={ @@ -1417,7 +1410,7 @@ export function TransferPickupDetails({ </PurchaseDetailsTable> ); } -export function WithdrawDetails({ amount }: { amount: AmountWithFee }): VNode { +export function WithdrawDetails({ conversion, amount }: { conversion?: AmountJson, amount: AmountWithFee }): VNode { const { i18n } = useTranslationContext(); const maxFrac = [amount.fee, amount.fee] @@ -1428,15 +1421,36 @@ export function WithdrawDetails({ amount }: { amount: AmountWithFee }): VNode { return ( <PurchaseDetailsTable> - <tr> - <td> - <i18n.Translate>Transfer</i18n.Translate> - </td> - <td> - <Amount value={amount.value} maxFracSize={amount.maxFrac} /> - </td> - </tr> - + {conversion ? + <Fragment> + <tr> + <td> + <i18n.Translate>Transfer</i18n.Translate> + </td> + <td> + <Amount value={conversion} maxFracSize={amount.maxFrac} /> + </td> + </tr> + {conversion.fraction === amount.value.fraction && conversion.value === amount.value.value ? undefined : + <tr> + <td> + <i18n.Translate>Converted</i18n.Translate> + </td> + <td> + <Amount value={amount.value} maxFracSize={amount.maxFrac} /> + </td> + </tr> + } + </Fragment> + : <tr> + <td> + <i18n.Translate>Transfer</i18n.Translate> + </td> + <td> + <Amount value={amount.value} maxFracSize={amount.maxFrac} /> + </td> + </tr> + } {Amounts.isNonZero(amount.fee) && ( <tr> <td> @@ -2007,7 +2021,7 @@ function ShowWithdrawalDetailForBankIntegrated({ {showDetails && ( <BankDetailsByPaytoType amount={raw} - exchangeBaseUrl={transaction.exchangeBaseUrl} + accounts={transaction.withdrawalDetails.exchangeCreditAccountDetails ?? []} subject={transaction.withdrawalDetails.reservePub} /> )} |