diff --git a/packages/taler-util/src/amounts.ts b/packages/taler-util/src/amounts.ts index cd74cda35..d389e3ed3 100644 --- a/packages/taler-util/src/amounts.ts +++ b/packages/taler-util/src/amounts.ts @@ -431,7 +431,9 @@ export class Amounts { } } - const currencyFormatter = new Intl.NumberFormat("en-US"); - return currencyFormatter.format(Number(s)); + const currencyFormatter = new Intl.NumberFormat("en-US", { + minimumFractionDigits: minFractional, + }); + return currencyFormatter.format(s as any); } } diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index d73d593e8..87200c2f9 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -138,7 +138,7 @@ async function handleExchangeUpdateError( } } -function getExchangeRequestTimeout(e: ExchangeRecord): Duration { +export function getExchangeRequestTimeout(): Duration { return { d_ms: 5000 }; } @@ -199,6 +199,27 @@ getExchangeDetails.makeContext = (db: DbAccess) => exchangeDetails: x.exchangeDetails, })); +export async function updateExchangeTermsOfService( + ws: InternalWalletState, + exchangeBaseUrl: string, + tos: ExchangeTosDownloadResult, +): Promise { + await ws.db + .mktx((x) => ({ + exchanges: x.exchanges, + exchangeDetails: x.exchangeDetails, + })) + .runReadWrite(async (tx) => { + const d = await getExchangeDetails(tx, exchangeBaseUrl); + if (d) { + d.termsOfServiceText = tos.tosText; + d.termsOfServiceContentType = tos.tosContentType; + d.termsOfServiceLastEtag = tos.tosEtag; + await tx.exchangeDetails.put(d); + } + }); +} + export async function acceptExchangeTermsOfService( ws: InternalWalletState, exchangeBaseUrl: string, @@ -434,6 +455,36 @@ async function downloadKeysInfo( }; } +export async function downloadTosFromAcceptedFormat( + ws: InternalWalletState, + baseUrl: string, + timeout: Duration, + acceptedFormat?: string[]): Promise { + let tosFound: ExchangeTosDownloadResult | undefined; + //Remove this when exchange supports multiple content-type in accept header + if (acceptedFormat) + for (const format of acceptedFormat) { + const resp = await downloadExchangeWithTermsOfService( + baseUrl, + ws.http, + timeout, + format, + ); + if (resp.tosContentType === format) { + tosFound = resp; + break; + } + } + if (tosFound !== undefined) return tosFound + // If none of the specified format was found try text/plain + return await downloadExchangeWithTermsOfService( + baseUrl, + ws.http, + timeout, + "text/plain", + ); +} + /** * Update or add exchange DB entry by fetching the /keys and /wire information. * Optionally link the reserve entry to the new or existing @@ -479,7 +530,7 @@ async function updateExchangeFromUrlImpl( logger.info("updating exchange /keys info"); - const timeout = getExchangeRequestTimeout(r); + const timeout = getExchangeRequestTimeout(); const keysInfo = await downloadKeysInfo(baseUrl, ws.http, timeout); @@ -507,33 +558,10 @@ async function updateExchangeFromUrlImpl( logger.info("finished validating exchange /wire info"); - let tosFound: ExchangeTosDownloadResult | undefined; - //Remove this when exchange supports multiple content-type in accept header - if (acceptedFormat) - for (const format of acceptedFormat) { - const resp = await downloadExchangeWithTermsOfService( - baseUrl, - ws.http, - timeout, - format, - ); - if (resp.tosContentType === format) { - tosFound = resp; - break; - } - } - // If none of the specified format was found try text/plain - const tosDownload = - tosFound !== undefined - ? tosFound - : await downloadExchangeWithTermsOfService( - baseUrl, - ws.http, - timeout, - "text/plain", - ); - let recoupGroupId: string | undefined = undefined; + const tosDownload = await downloadTosFromAcceptedFormat(ws, baseUrl, timeout, acceptedFormat) + + let recoupGroupId: string | undefined; logger.trace("updating exchange info in database"); diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 182453ff2..b53ba24c4 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -89,9 +89,12 @@ import { } from "./operations/deposits.js"; import { acceptExchangeTermsOfService, + downloadTosFromAcceptedFormat, getExchangeDetails, + getExchangeRequestTimeout, getExchangeTrust, - updateExchangeFromUrl + updateExchangeFromUrl, + updateExchangeTermsOfService } from "./operations/exchanges.js"; import { getMerchantInfo } from "./operations/merchants.js"; import { @@ -441,7 +444,6 @@ async function getExchangeTos( ws, exchangeBaseUrl, acceptedFormat, - true, ); const content = exchangeDetails.termsOfServiceText; const currentEtag = exchangeDetails.termsOfServiceLastEtag; @@ -453,12 +455,34 @@ async function getExchangeTos( ) { throw Error("exchange is in invalid state"); } + if (acceptedFormat && acceptedFormat.findIndex(f => f === contentType) !== -1) { + return { + acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag, + currentEtag, + content, + contentType, + }; + } + + const tosDownload = await downloadTosFromAcceptedFormat(ws, exchangeBaseUrl, getExchangeRequestTimeout(), acceptedFormat); + + if (tosDownload.tosContentType === contentType) { + return { + acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag, + currentEtag, + content, + contentType, + }; + } + await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload) + return { acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag, - currentEtag, - content, - contentType, + currentEtag: tosDownload.tosEtag, + content: tosDownload.tosText, + contentType: tosDownload.tosContentType, }; + } async function listKnownBankAccounts( @@ -1245,3 +1269,4 @@ class InternalWalletStateImpl implements InternalWalletState { } } } + diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index e507bf45b..c16bcb53b 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -43,7 +43,7 @@ export enum Pages { backup_provider_detail = "/backup/provider/:pid", backup_provider_add = "/backup/provider/add", - last_activity = "/last-activity", + pending = "/pending", settings = "/settings", settings_exchange_add = "/settings/exchange/add", @@ -84,10 +84,7 @@ export function NavBar({
{i18n.str`Balance`} - {i18n.str`Last Activity`} + {i18n.str`Pending`} {i18n.str`Backup`} {i18n.str`Settings`} {devMode && {i18n.str`Dev`}} diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx index 206dcb0c5..db26abec6 100644 --- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -15,6 +15,8 @@ */ import { + AmountJson, + Amounts, AmountString, Timestamp, Transaction, @@ -37,10 +39,7 @@ import { } from "./styled"; import { Time } from "./Time"; -export function TransactionItem(props: { - tx: Transaction; - multiCurrency: boolean; -}): VNode { +export function TransactionItem(props: { tx: Transaction }): VNode { const tx = props.tx; switch (tx.type) { case TransactionType.Withdrawal: @@ -53,7 +52,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageBank} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); case TransactionType.Payment: @@ -67,7 +65,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageShoppingCart} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); case TransactionType.Refund: @@ -80,7 +77,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageRefund} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); case TransactionType.Tip: @@ -93,7 +89,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageHandHeart} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); case TransactionType.Refresh: @@ -106,7 +101,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageRefresh} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); case TransactionType.Deposit: @@ -119,7 +113,6 @@ export function TransactionItem(props: { timestamp={tx.timestamp} iconPath={imageRefresh} pending={tx.pending} - multiCurrency={props.multiCurrency} /> ); } @@ -144,13 +137,12 @@ function TransactionLayout(props: TransactionLayoutProps): VNode { )} - @@ -166,18 +158,15 @@ interface TransactionLayoutProps { id: string; iconPath: string; pending: boolean; - multiCurrency: boolean; } interface TransactionAmountProps { debitCreditIndicator: "debit" | "credit" | "unknown"; - amount: AmountString | "unknown"; + amount: AmountJson; pending: boolean; - multiCurrency: boolean; } function TransactionAmount(props: TransactionAmountProps): VNode { - const [currency, amount] = props.amount.split(":"); let sign: string; switch (props.debitCreditIndicator) { case "credit": @@ -204,9 +193,8 @@ function TransactionAmount(props: TransactionAmountProps): VNode { > {sign} - {amount} + {Amounts.stringifyValue(props.amount)} - {props.multiCurrency &&
{currency}
} {props.pending &&
PENDING
} ); diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 2d16b496c..5dd7318b7 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -77,7 +77,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` justify-content: space-between; align-items: center; & > * { - width: 400px; + width: 500px; } & > section { padding: ${({ noPadding }) => (noPadding ? "0px" : "8px")}; @@ -142,7 +142,7 @@ export const Middle = styled.div` export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>` height: 290px; - width: ${({ devMode }) => (!devMode ? "400px" : "500px")}; + width: 500px; display: flex; flex-direction: column; justify-content: space-between; @@ -783,7 +783,7 @@ export const PopupNavigation = styled.div<{ devMode?: boolean }>` display: flex; & > div { - width: ${({ devMode }) => (!devMode ? "400px" : "500px")}; + width: 500px; } & > div > a { @@ -815,15 +815,16 @@ export const NiceSelect = styled.div` box-shadow: none; background-image: ${image}; - background-position: right 8px center; + background-position: right 4px center; background-repeat: no-repeat; - background-size: 1.5em 1.5em; + background-size: 32px 32px; background-color: white; border-radius: 0.25rem; font-size: 1em; - padding: 0.5em 3em 0.5em 1em; + padding: 8px 32px 8px 8px; + /* 0.5em 3em 0.5em 1em; */ cursor: pointer; } diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index f7174c3c5..5169c8540 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -39,7 +39,7 @@ import { SettingsPage } from "./popup/Settings"; import { TalerActionFound } from "./popup/TalerActionFound"; import { ExchangeAddPage } from "./wallet/ExchangeAddPage"; import { IoCProviderForRuntime } from "./context/iocContext"; -import { LastActivityPage } from "./wallet/LastActivityPage"; +import { Pending } from "./wallet/PendingPage"; import { Match } from "preact-router/match"; function main(): void { @@ -125,7 +125,7 @@ function Application(): VNode { }} /> - + 1; - if (balances.length === 0 || !selectedCurrency) { return ( @@ -143,52 +143,73 @@ export function HistoryView({ return (
-

- {currencies.length === 1 ? ( -

{selectedCurrency}
- ) : ( - - { + setCurrencyIndex(Number(e.currentTarget.value)); + }} + > + {currencies.map((currency, index) => { + return ( + + ); + })} + + + )} + {currencyAmount && ( + - {currencies.map((currency, index) => { - return ( - - ); - })} - - - )} - {currencyAmount && ( -

- {Amounts.stringifyValue(currencyAmount)} -

- )} -

-
- goToWalletManualWithdraw(selectedCurrency)} - > - Withdraw - - {currencyAmount && Amounts.isNonZero(currencyAmount) && ( - goToWalletDeposit(selectedCurrency)} + {Amounts.stringifyValue(currencyAmount)} + + )} +
+
+ goToWalletManualWithdraw(selectedCurrency)} > - Deposit - - )} + Withdraw + + {currencyAmount && Amounts.isNonZero(currencyAmount) && ( + goToWalletDeposit(selectedCurrency)} + > + Deposit + + )} +
{datesWithTransaction.length === 0 ? ( @@ -205,11 +226,7 @@ export function HistoryView({ /> {byDate[d].map((tx, i) => ( - + ))} ); diff --git a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx b/packages/taler-wallet-webextension/src/wallet/PendingPage.stories.tsx similarity index 90% rename from packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx rename to packages/taler-wallet-webextension/src/wallet/PendingPage.stories.tsx index e729c2982..cbcb5a824 100644 --- a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/PendingPage.stories.tsx @@ -21,10 +21,10 @@ import { createExample } from "../test-utils"; import { queryToSlashKeys } from "../utils/index"; -import { LastActivityPage as TestedComponent } from "./LastActivityPage"; +import { Pending as TestedComponent } from "./PendingPage"; export default { - title: "wallet/last activity", + title: "wallet/pending", component: TestedComponent, }; diff --git a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx b/packages/taler-wallet-webextension/src/wallet/PendingPage.tsx similarity index 96% rename from packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx rename to packages/taler-wallet-webextension/src/wallet/PendingPage.tsx index 8ec4c8759..998095238 100644 --- a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/PendingPage.tsx @@ -19,7 +19,7 @@ import { useState } from "preact/hooks"; import { ButtonPrimary } from "../components/styled"; import { AddNewActionView } from "./AddNewActionView"; -export function LastActivityPage(): VNode { +export function Pending(): VNode { const [addingAction, setAddingAction] = useState(false); if (addingAction) { diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index dbcf053e2..c8bbc7f7a 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -38,12 +38,11 @@ import { strings } from "./i18n/strings"; import { NavBar, Pages } from "./NavigationBar"; import { DeveloperPage } from "./popup/DeveloperPage"; import { BackupPage } from "./wallet/BackupPage"; -import { BalancePage } from "./wallet/BalancePage"; import { DepositPage } from "./wallet/DepositPage"; import { ExchangeAddPage } from "./wallet/ExchangeAddPage"; import { HistoryPage } from "./wallet/History"; -import { LastActivityPage } from "./wallet/LastActivityPage"; import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage"; +import { Pending } from "./wallet/PendingPage"; import { ProviderAddPage } from "./wallet/ProviderAddPage"; import { ProviderDetailPage } from "./wallet/ProviderDetailPage"; import { SettingsPage } from "./wallet/Settings"; @@ -124,19 +123,10 @@ function Application(): VNode { - route( - Pages.balance_manual_withdraw.replace(":currency?", ""), - ) - } - goToWalletDeposit={(currency: string) => - route(Pages.balance_deposit.replace(":currency", currency)) - } - goToWalletHistory={(currency: string) => - route(Pages.balance_history.replace(":currency", currency)) - } + component={Redirect} + to={Pages.balance_history.replace(":currency", "")} /> + {/** - * LAST ACTIVITY + * PENDING */} - + {/** @@ -246,7 +233,11 @@ function Application(): VNode { {/** * NOT FOUND */} - +