aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/manifest-common.json4
-rw-r--r--packages/taler-wallet-webextension/manifest-v2.json4
-rw-r--r--packages/taler-wallet-webextension/package.json2
-rw-r--r--packages/taler-wallet-webextension/src/NavigationBar.tsx116
-rw-r--r--packages/taler-wallet-webextension/src/components/BalanceTable.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx418
-rw-r--r--packages/taler-wallet-webextension/src/components/Modal.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/components/MultiActionButton.tsx18
-rw-r--r--packages/taler-wallet-webextension/src/components/SelectList.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/components/ShowBanksForPaytoPopup.tsx61
-rw-r--r--packages/taler-wallet-webextension/src/components/ShowQRsForPaytoPopup.tsx94
-rw-r--r--packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx26
-rw-r--r--packages/taler-wallet-webextension/src/components/WalletActivity.tsx33
-rw-r--r--packages/taler-wallet-webextension/src/components/styled/index.tsx32
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/index.ts10
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/state.ts70
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/test.ts73
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/views.tsx40
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts7
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts82
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx9
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts5
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts6
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts98
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx9
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/index.ts28
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/state.ts161
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx43
-rw-r--r--packages/taler-wallet-webextension/src/cta/termsExample.ts23
-rw-r--r--packages/taler-wallet-webextension/src/i18n/de.po89
-rw-r--r--packages/taler-wallet-webextension/src/i18n/es.po8
-rw-r--r--packages/taler-wallet-webextension/src/i18n/tr.po232
-rw-r--r--packages/taler-wallet-webextension/src/i18n/uk.po693
-rw-r--r--packages/taler-wallet-webextension/src/platform/chrome.ts17
-rw-r--r--packages/taler-wallet-webextension/src/platform/firefox.ts3
-rw-r--r--packages/taler-wallet-webextension/src/popup/Application.tsx64
-rw-r--r--packages/taler-wallet-webextension/src/popup/BalancePage.tsx29
-rw-r--r--packages/taler-wallet-webextension/src/pwa/index.html91
-rw-r--r--packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts10
-rw-r--r--packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts30
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddExchange/index.ts18
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddExchange/state.ts16
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddExchange/views.tsx3
-rw-r--r--packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Application.tsx600
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BackupPage.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts12
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts55
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts50
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts30
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts178
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx18
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts57
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx126
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx21
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.stories.tsx80
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.tsx67
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts6
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts67
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/wallet/QrReader.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/wallet/SupportedBanksForAccount.tsx60
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx19
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx109
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts4
68 files changed, 2590 insertions, 1794 deletions
diff --git a/packages/taler-wallet-webextension/manifest-common.json b/packages/taler-wallet-webextension/manifest-common.json
index 01248e964..bea19dbff 100644
--- a/packages/taler-wallet-webextension/manifest-common.json
+++ b/packages/taler-wallet-webextension/manifest-common.json
@@ -2,7 +2,7 @@
"name": "GNU Taler Wallet (git)",
"description": "Privacy preserving and transparent payments",
"author": "GNU Taler Developers",
- "version": "0.12.2",
+ "version": "0.13.6",
"icons": {
"16": "static/img/taler-logo-16.png",
"19": "static/img/taler-logo-19.png",
@@ -14,5 +14,5 @@
"256": "static/img/taler-logo-256.png",
"512": "static/img/taler-logo-512.png"
},
- "version_name": "0.12.2"
+ "version_name": "0.13.6"
}
diff --git a/packages/taler-wallet-webextension/manifest-v2.json b/packages/taler-wallet-webextension/manifest-v2.json
index 6f2096b05..459277d3d 100644
--- a/packages/taler-wallet-webextension/manifest-v2.json
+++ b/packages/taler-wallet-webextension/manifest-v2.json
@@ -5,7 +5,7 @@
"browser_specific_settings": {
"gecko": {
"id": "wallet@taler.net",
- "strict_min_version": "57.0"
+ "strict_min_version": "58.0"
}
},
"commands": {
@@ -82,4 +82,4 @@
"page": "static/background.html",
"persistent": true
}
-} \ No newline at end of file
+}
diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json
index 5c622da70..5bdf5ec1b 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -1,6 +1,6 @@
{
"name": "@gnu-taler/taler-wallet-webextension",
- "version": "0.12.2",
+ "version": "0.13.6",
"description": "GNU Taler Wallet browser extension",
"main": "./build/index.js",
"types": "./build/index.d.ts",
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index fe348f7fb..7fb8dad8b 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -39,7 +39,10 @@ import qrIcon from "./svg/qr_code_24px.inline.svg";
import settingsIcon from "./svg/settings_black_24dp.inline.svg";
import warningIcon from "./svg/warning_24px.inline.svg";
import { parseTalerUri, TalerUriAction } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ encodeCrockForURI,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
/**
* List of pages used by the wallet
@@ -60,10 +63,7 @@ function replaceAll(
): string {
let result = pattern;
for (const v in vars) {
- result = result.replace(
- vars[v],
- !values[v] ? "" : encodeURIComponent(values[v]),
- );
+ result = result.replace(vars[v], !values[v] ? "" : values[v]);
}
return result;
}
@@ -94,23 +94,43 @@ function pageDefinition<T extends object>(pattern: string): PageLocation<T> {
return f;
}
+/**
+ * taler action and scope info may contain
+ * character not suitable for URL
+ */
+type CrockEncodedString = string;
+
export const Pages = {
welcome: "/welcome",
balance: "/balance",
- balanceHistory: pageDefinition<{ currency?: string }>(
- "/balance/history/:currency?",
- ),
- searchHistory: pageDefinition<{ currency?: string }>(
- "/search/history/:currency?",
+ balanceHistory: pageDefinition<{ scope?: CrockEncodedString }>(
+ "/balance/history/:scope?",
),
- balanceDeposit: pageDefinition<{ amount: string }>(
- "/balance/deposit/:amount",
+ searchHistory: pageDefinition<{ scope?: CrockEncodedString }>(
+ "/search/history/:scope?",
),
balanceTransaction: pageDefinition<{ tid: string }>(
"/balance/transaction/:tid",
),
- sendCash: pageDefinition<{ amount?: string }>("/destination/send/:amount"),
- receiveCash: pageDefinition<{ amount?: string }>("/destination/get/:amount?"),
+ bankManange: pageDefinition<{ scope: CrockEncodedString }>(
+ "/bank/manage/:scope",
+ ),
+ balanceDeposit: pageDefinition<{
+ scope: CrockEncodedString;
+ }>("/balance/deposit/:scope"),
+ sendCash: pageDefinition<{ scope: CrockEncodedString; amount?: string }>(
+ "/destination/send/:scope/:amount?",
+ ),
+ // if no scope is specified, then exchange selection page will be shown
+ receiveCash: pageDefinition<{ scope?: CrockEncodedString; amount?: string }>(
+ "/destination/get/:scope?/:amount?",
+ ),
+ receiveCashForPurchase: pageDefinition<{ id?: string }>(
+ "/add-for-payment/purchase/:id",
+ ),
+ receiveCashForInvoice: pageDefinition<{ id?: string }>(
+ "/add-for-payment/purchase/:id",
+ ),
dev: "/dev",
exchanges: "/exchanges",
@@ -127,26 +147,40 @@ export const Pages = {
"/settings/exchange/add/:currency?",
),
- defaultCta: pageDefinition<{ uri: string }>("/taler-uri/:uri"),
- cta: pageDefinition<{ action: string }>("/cta/:action"),
+ defaultCta: pageDefinition<{ uri: CrockEncodedString }>("/taler-uri/:uri"),
+ // FIXME: mem leak problems
+ defaultCtaSimple: pageDefinition<{ uri: CrockEncodedString }>(
+ "/taler-uri-simple/:uri",
+ ),
+ cta: pageDefinition<{ action: CrockEncodedString }>("/cta/:action"),
ctaPay: "/cta/pay",
ctaPayTemplate: "/cta/pay/template",
ctaRecovery: "/cta/recovery",
ctaRefund: "/cta/refund",
ctaWithdraw: "/cta/withdraw",
- ctaDeposit: "/cta/deposit",
+ ctaDeposit: pageDefinition<{
+ scope: CrockEncodedString;
+ account: CrockEncodedString;
+ }>("/cta/deposit/:scope/:account"),
ctaExperiment: "/cta/experiment",
ctaAddExchange: "/cta/add/exchange",
- ctaInvoiceCreate: pageDefinition<{ amount?: string }>(
- "/cta/invoice/create/:amount?",
- ),
- ctaTransferCreate: pageDefinition<{ amount?: string }>(
- "/cta/transfer/create/:amount?",
- ),
+ ctaInvoiceCreate: pageDefinition<{
+ scope: CrockEncodedString;
+ }>("/cta/invoice/create/:scope/:amount?"),
+ ctaTransferCreate: pageDefinition<{
+ scope: CrockEncodedString;
+ }>("/cta/transfer/create/:scope/:amount?"),
ctaInvoicePay: "/cta/invoice/pay",
ctaTransferPickup: "/cta/transfer/pickup",
- ctaWithdrawManual: pageDefinition<{ amount?: string }>(
- "/cta/manual-withdraw/:amount?",
+
+ ctaWithdrawManualForScope: pageDefinition<{
+ scope: CrockEncodedString;
+ amount?: string;
+ }>("/cta/scope-withdraw/:scope/:amount?"),
+ ctaWithdrawManual: pageDefinition<{amount?: string;}>("/cta/manual-withdraw/:amount?"),
+ paytoQrs: pageDefinition<{ payto: CrockEncodedString }>("/payto/qrs/:payto?"),
+ paytoBanks: pageDefinition<{ payto: CrockEncodedString }>(
+ "/payto/banks/:payto?",
),
};
@@ -178,7 +212,7 @@ export function getPathnameForTalerURI(talerUri: string): string | undefined {
typeof Pages[pageName] === "function"
? (Pages[pageName] as any)()
: Pages[pageName];
- return `${pageString}?talerUri=${encodeURIComponent(talerUri)}`;
+ return `${pageString}?talerUri=${encodeCrockForURI(talerUri)}`;
}
export type PopupNavBarOptions = "balance" | "backup" | "dev";
@@ -195,17 +229,17 @@ export function PopupNavBar({ path }: { path?: PopupNavBarOptions }): VNode {
const { i18n } = useTranslationContext();
return (
<NavigationHeader>
- <a href={Pages.balance} class={path === "balance" ? "active" : ""}>
+ <a href={`#${Pages.balance}`} class={path === "balance" ? "active" : ""}>
<i18n.Translate>Balance</i18n.Translate>
</a>
<EnabledBySettings name="backup">
- <a href={Pages.backup} class={path === "backup" ? "active" : ""}>
+ <a href={`#${Pages.backup}`} class={path === "backup" ? "active" : ""}>
<i18n.Translate>Backup</i18n.Translate>
</a>
</EnabledBySettings>
<div style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}>
{attentionCount > 0 ? (
- <a href={Pages.notifications}>
+ <a href={`#${Pages.notifications}`}>
<SvgIcon
title={i18n.str`Notifications`}
dangerouslySetInnerHTML={{ __html: warningIcon }}
@@ -215,14 +249,14 @@ export function PopupNavBar({ path }: { path?: PopupNavBarOptions }): VNode {
) : (
<Fragment />
)}
- <a href={Pages.qr}>
+ <a href={`#${Pages.qr}`}>
<SvgIcon
title={i18n.str`QR Reader and Taler URI`}
dangerouslySetInnerHTML={{ __html: qrIcon }}
color="white"
/>
</a>
- <a href={Pages.settings}>
+ <a href={`#${Pages.settings}`}>
<SvgIcon
title={i18n.str`Settings`}
dangerouslySetInnerHTML={{ __html: settingsIcon }}
@@ -250,17 +284,23 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
return (
<NavigationHeaderHolder>
<NavigationHeader>
- <a href={Pages.balance} class={path === "balance" ? "active" : ""}>
+ <a
+ href={`#${Pages.balance}`}
+ class={path === "balance" ? "active" : ""}
+ >
<i18n.Translate>Balance</i18n.Translate>
</a>
<EnabledBySettings name="backup">
- <a href={Pages.backup} class={path === "backup" ? "active" : ""}>
+ <a
+ href={`#${Pages.backup}`}
+ class={path === "backup" ? "active" : ""}
+ >
<i18n.Translate>Backup</i18n.Translate>
</a>
</EnabledBySettings>
{attentionCount > 0 ? (
- <a href={Pages.notifications}>
+ <a href={`#${Pages.notifications}`}>
<i18n.Translate>Notifications</i18n.Translate>
</a>
) : (
@@ -268,7 +308,7 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
)}
<EnabledBySettings name="advancedMode">
- <a href={Pages.dev} class={path === "dev" ? "active" : ""}>
+ <a href={`#${Pages.dev}`} class={path === "dev" ? "active" : ""}>
<i18n.Translate>Dev tools</i18n.Translate>
</a>
</EnabledBySettings>
@@ -276,21 +316,21 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
<div
style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}
>
- <a href={Pages.searchHistory({})}>
+ <a href={`#${Pages.searchHistory({})}`}>
<SvgIcon
title={i18n.str`Search transactions`}
dangerouslySetInnerHTML={{ __html: searchIcon }}
color="white"
/>
</a>
- <a href={Pages.qr}>
+ <a href={`#${Pages.qr}`}>
<SvgIcon
title={i18n.str`QR Reader and Taler URI`}
dangerouslySetInnerHTML={{ __html: qrIcon }}
color="white"
/>
</a>
- <a href={Pages.settings}>
+ <a href={`#${Pages.settings}`}>
<SvgIcon
title={i18n.str`Settings`}
dangerouslySetInnerHTML={{ __html: settingsIcon }}
diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
index 6dd577b88..a6ccc10ca 100644
--- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
+++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts, ScopeType, WalletBalance } from "@gnu-taler/taler-util";
+import { Amounts, ScopeInfo, ScopeType, WalletBalance } from "@gnu-taler/taler-util";
import { Fragment, VNode, h } from "preact";
import {
TableWithRoundRows as TableWithRoundedRows
@@ -25,7 +25,7 @@ export function BalanceTable({
goToWalletHistory,
}: {
balances: WalletBalance[];
- goToWalletHistory: (currency: string) => void;
+ goToWalletHistory: (currency: ScopeInfo) => void;
}): VNode {
return (
<Fragment>
@@ -36,7 +36,7 @@ export function BalanceTable({
return (
<tr
key={idx}
- onClick={() => goToWalletHistory(av.currency)}
+ onClick={() => goToWalletHistory(entry.scopeInfo)}
style={{ cursor: "pointer" }}
>
<td>{av.currency}</td>
diff --git a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
index 8b6377fc5..65368fd81 100644
--- a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
+++ b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
@@ -17,19 +17,34 @@
import {
AmountJson,
Amounts,
+ AmountString,
parsePaytoUri,
+ PaytoUriIBAN,
+ PaytoUriTalerBank,
+ PaytoUriUnknown,
segwitMinAmount,
stringifyPaytoUri,
TranslatedString,
WithdrawalExchangeAccountDetails,
} from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ encodeCrockForURI,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
+import { Button } from "../mui/Button.js";
import { CopiedIcon, CopyIcon } from "../svg/index.js";
import { Amount } from "./Amount.js";
import { ButtonBox, TooltipLeft, WarningBox } from "./styled/index.js";
-import { Button } from "../mui/Button.js";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useBackendContext } from "../context/backend.js";
+import { QR } from "./QR.js";
+import { Pages } from "../NavigationBar.js";
+import { ShowQRsForPaytoPopup } from "./ShowQRsForPaytoPopup.js";
+import { SafeHandler } from "../mui/handlers.js";
+import { ShowBanksForPaytoPopup } from "./ShowBanksForPaytoPopup.js";
export interface BankDetailsProps {
subject: string;
@@ -59,71 +74,12 @@ export function BankDetailsByPaytoType({
const payto = parsePaytoUri(selectedAccount.paytoUri);
if (!payto) return <Fragment />;
+ // make sure the payto has the right params
payto.params["amount"] = altCurrency
? selectedAccount.transferAmount!
: Amounts.stringify(amount);
payto.params["message"] = subject;
- function Frame({
- title,
- children,
- }: {
- title: TranslatedString;
- children: ComponentChildren;
- }): VNode {
- return (
- <section
- style={{
- textAlign: "left",
- border: "solid 1px black",
- padding: 8,
- borderRadius: 4,
- }}
- >
- <div
- style={{
- display: "flex",
- width: "100%",
- justifyContent: "space-between",
- }}
- >
- <p style={{ marginTop: 0 }}>{title}</p>
- <div></div>
- </div>
-
- {children}
-
- {accounts.length > 1 ? (
- <Fragment>
- {accounts.map((ac, acIdx) => {
- const accountLabel = ac.bankLabel ?? `Account #${acIdx + 1}`;
- return (
- <Button
- key={acIdx}
- variant={acIdx === index ? "contained" : "outlined"}
- onClick={async () => {
- setIndex(acIdx);
- }}
- >
- {accountLabel} (
- {ac.currencySpecification?.name ?? amount.currency})
- </Button>
- );
- })}
-
- {/* <Button variant={currency === altCurrency ? "contained" : "outlined"}
- onClick={async () => {
- setCurrency(altCurrency)
- }}
- >
- <i18n.Translate>{altCurrency}</i18n.Translate>
- </Button> */}
- </Fragment>
- ) : undefined}
- </section>
- );
- }
-
if (payto.isKnown && payto.targetType === "bitcoin") {
const min = segwitMinAmount(amount.currency);
const addrs = payto.segwitAddrs.map(
@@ -132,7 +88,13 @@ export function BankDetailsByPaytoType({
addrs.unshift(`${payto.targetPath} ${Amounts.stringifyValue(amount)}`);
const copyContent = addrs.join("\n");
return (
- <Frame title={i18n.str`Bitcoin transfer details`}>
+ <Frame
+ title={i18n.str`Bitcoin transfer details`}
+ accounts={accounts}
+ updateIndex={setIndex}
+ currentIndex={index}
+ defaultCurrency={amount.currency}
+ >
<p>
<i18n.Translate>
The exchange need a transaction with 3 output, one output is the
@@ -176,6 +138,47 @@ export function BankDetailsByPaytoType({
);
}
+ return (
+ <Frame
+ title={i18n.str`Bank transfer details`}
+ accounts={accounts}
+ updateIndex={setIndex}
+ currentIndex={index}
+ defaultCurrency={amount.currency}
+ >
+ <IBANAccountInfoTable payto={payto} subject={subject} />
+ </Frame>
+ );
+}
+
+function IBANAccountInfoTable({
+ payto,
+ subject,
+}: {
+ subject: string;
+ payto: PaytoUriUnknown | PaytoUriIBAN | PaytoUriTalerBank;
+}) {
+ const { i18n } = useTranslationContext();
+ const api = useBackendContext();
+ const [showBanks, setShowBanks] = useState(false);
+ const [showQrs, setShowQrs] = useState(false);
+
+ const hook = useAsyncAsHook(async () => {
+ const qrs = await api.wallet.call(WalletApiOperation.GetQrCodesForPayto, {
+ paytoUri: stringifyPaytoUri(payto),
+ });
+ const banks = await api.wallet.call(
+ WalletApiOperation.GetBankingChoicesForPayto,
+ {
+ paytoUri: stringifyPaytoUri(payto),
+ },
+ );
+ return { qrs, banks };
+ }, []);
+
+ const qrCodes = !hook || hook.hasError ? [] : hook.response.qrs.codes;
+ const banksSites = !hook || hook.hasError ? [] : hook.response.banks.choices;
+
const accountPart = !payto.isKnown ? (
<Fragment>
<Row name={i18n.str`Account`} value={payto.targetPath} />
@@ -196,96 +199,147 @@ export function BankDetailsByPaytoType({
const receiver =
payto.params["receiver-name"] || payto.params["receiver"] || undefined;
+
return (
- <Frame title={i18n.str`Bank transfer details`}>
- <table>
- <tbody>
- <tr>
- <td colSpan={3}>
- <i18n.Translate>Step 1:</i18n.Translate>
- &nbsp;
- <i18n.Translate>
- Copy this code and paste it into the subject/purpose field in
- your banking app or bank website
- </i18n.Translate>
- </td>
- </tr>
- <Row name={i18n.str`Subject`} value={subject} literal />
+ <table>
+ <tbody>
+ <tr>
+ <td colSpan={3}>
+ <i18n.Translate>Step 1:</i18n.Translate>
+ &nbsp;
+ <i18n.Translate>
+ Copy this code and paste it into the subject/purpose field in your
+ banking app or bank website
+ </i18n.Translate>
+ </td>
+ </tr>
+ <Row name={i18n.str`Subject`} value={subject} literal />
- <tr>
- <td colSpan={3}>
- <i18n.Translate>Step 2:</i18n.Translate>
- &nbsp;
- <i18n.Translate>
- If you don't already have it in your banking favourites list,
- then copy and paste this IBAN and the name into the receiver
- fields in your banking app or website
- </i18n.Translate>
- </td>
- </tr>
- {accountPart}
- {receiver ? (
- <Row name={i18n.str`Receiver name`} value={receiver} />
- ) : undefined}
+ <tr>
+ <td colSpan={3}>
+ <i18n.Translate>Step 2:</i18n.Translate>
+ &nbsp;
+ <i18n.Translate>
+ If you don't already have it in your banking favourites list, then
+ copy and paste this IBAN and the name into the receiver fields in
+ your banking app or website
+ </i18n.Translate>
+ </td>
+ </tr>
+ {accountPart}
+ {receiver ? (
+ <Row name={i18n.str`Receiver name`} value={receiver} />
+ ) : undefined}
- <tr>
- <td colSpan={3}>
- <i18n.Translate>Step 3:</i18n.Translate>
- &nbsp;
- <i18n.Translate>
- Finish the wire transfer setting the amount in your banking app
- or website, then this withdrawal will proceed automatically.
- </i18n.Translate>
- </td>
- </tr>
- <Row
- name={i18n.str`Amount`}
- value={
- <Amount
- value={altCurrency ? selectedAccount.transferAmount! : amount}
- hideCurrency
- />
- }
- />
+ <tr>
+ <td colSpan={3}>
+ <i18n.Translate>Step 3:</i18n.Translate>
+ &nbsp;
+ <i18n.Translate>
+ Finish the wire transfer setting the amount in your banking app or
+ website, then this withdrawal will proceed automatically.
+ </i18n.Translate>
+ </td>
+ </tr>
+ <Row
+ name={i18n.str`Amount`}
+ value={
+ <Amount
+ value={payto.params["amount"] as AmountString}
+ hideCurrency
+ />
+ }
+ />
- <tr>
- <td colSpan={3}>
- <WarningBox style={{ margin: 0 }}>
- <span>
- <i18n.Translate>
- Make sure ALL data is correct, including the subject;
- otherwise, the money will not arrive in this wallet. You can
- use the copy buttons (<CopyIcon />) to prevent typing errors
- or the "payto://" URI below to copy just one value.
- </i18n.Translate>
- </span>
- </WarningBox>
- </td>
- </tr>
+ <tr>
+ <td colSpan={3}>
+ <WarningBox style={{ margin: 0 }}>
+ <span>
+ <i18n.Translate>
+ Make sure ALL data is correct, including the subject;
+ otherwise, the money will not arrive in this wallet. You can
+ use the copy buttons (<CopyIcon />) to prevent typing errors
+ or the "payto://" URI below to copy just one value.
+ </i18n.Translate>
+ </span>
+ </WarningBox>
+ </td>
+ </tr>
- <tr>
- <td colSpan={2} width="100%" style={{ wordBreak: "break-all" }}>
- <i18n.Translate>
- Alternative if your bank already supports PayTo URI, you can use
- this{" "}
- <a
- target="_bank"
- rel="noreferrer"
- title="RFC 8905 for designating targets for payments"
- href="https://tools.ietf.org/html/rfc8905"
- >
- PayTo URI
- </a>{" "}
- link instead
- </i18n.Translate>
- </td>
- <td>
- <CopyButton getContent={() => stringifyPaytoUri(payto)} />
- </td>
- </tr>
- </tbody>
- </table>
- </Frame>
+ <tr>
+ <td colSpan={3} width="100%" style={{ wordBreak: "break-all" }}>
+ <i18n.Translate>
+ Alternative if your bank already supports PayTo URI, you can use
+ this{" "}
+ <a
+ target="_bank"
+ rel="noreferrer"
+ title="RFC 8905 for designating targets for payments"
+ href="https://tools.ietf.org/html/rfc8905"
+ >
+ PayTo URI
+ </a>{" "}
+ link instead
+ </i18n.Translate>
+ </td>
+ <td>
+ <CopyButton getContent={() => stringifyPaytoUri(payto)} />
+ </td>
+ </tr>
+
+ {banksSites.length < 1 ? undefined : (
+ <Fragment>
+ <div>
+ <a
+ href="#"
+ onClick={(e) => {
+ setShowBanks(true);
+ e.preventDefault();
+ }}
+ >
+ <i18n.Translate>
+ Continue with banking app or website
+ </i18n.Translate>
+ </a>
+ </div>
+
+ {showBanks ? (
+ <ShowBanksForPaytoPopup
+ banks={banksSites}
+ onClose={{
+ onClick: (async () =>
+ setShowBanks(false)) as SafeHandler<void>,
+ }}
+ />
+ ) : undefined}
+ </Fragment>
+ )}
+
+ {qrCodes.length < 1 ? undefined : (
+ <Fragment>
+ <div>
+ <a
+ href="#"
+ onClick={(e) => {
+ setShowQrs(true);
+ e.preventDefault();
+ }}
+ >
+ <i18n.Translate>Show QR code</i18n.Translate>
+ </a>
+ </div>
+ {showQrs ? (
+ <ShowQRsForPaytoPopup
+ qrs={qrCodes}
+ onClose={{
+ onClick: (async () => setShowQrs(false)) as SafeHandler<void>,
+ }}
+ />
+ ) : undefined}
+ </Fragment>
+ )}
+ </tbody>
+ </table>
);
}
@@ -358,3 +412,71 @@ function Row({
</tr>
);
}
+
+function Frame({
+ title,
+ children,
+ accounts,
+ defaultCurrency,
+ currentIndex,
+ updateIndex,
+}: {
+ title: TranslatedString;
+ children: ComponentChildren;
+ currentIndex: number;
+ updateIndex: (idx: number) => void;
+ defaultCurrency: string;
+ accounts: WithdrawalExchangeAccountDetails[];
+}): VNode {
+ return (
+ <section
+ style={{
+ textAlign: "left",
+ border: "solid 1px black",
+ padding: 8,
+ borderRadius: 4,
+ }}
+ >
+ <div
+ style={{
+ display: "flex",
+ width: "100%",
+ justifyContent: "space-between",
+ }}
+ >
+ <p style={{ marginTop: 0 }}>{title}</p>
+ <div></div>
+ </div>
+
+ {children}
+
+ {accounts.length > 1 ? (
+ <Fragment>
+ {accounts.map((ac, acIdx) => {
+ const accountLabel = ac.bankLabel ?? `Account #${acIdx + 1}`;
+ return (
+ <Button
+ key={acIdx}
+ variant={acIdx === currentIndex ? "contained" : "outlined"}
+ onClick={async () => {
+ updateIndex(acIdx);
+ }}
+ >
+ {accountLabel} (
+ {ac.currencySpecification?.name ?? defaultCurrency})
+ </Button>
+ );
+ })}
+
+ {/* <Button variant={currency === altCurrency ? "contained" : "outlined"}
+ onClick={async () => {
+ setCurrency(altCurrency)
+ }}
+ >
+ <i18n.Translate>{altCurrency}</i18n.Translate>
+ </Button> */}
+ </Fragment>
+ ) : undefined}
+ </section>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/components/Modal.tsx b/packages/taler-wallet-webextension/src/components/Modal.tsx
index c5f716c76..9023761bf 100644
--- a/packages/taler-wallet-webextension/src/components/Modal.tsx
+++ b/packages/taler-wallet-webextension/src/components/Modal.tsx
@@ -52,7 +52,7 @@ const Body = styled.div`
export function Modal({ title, children, onClose }: Props): VNode {
return (
- <div style={{ top: 0, position: "fixed", width: "100%", height: "100%" }}>
+ <div style={{ top: 0, left: 0, position: "fixed", width: "100%", height: "100%" }}>
<FullSize onClick={onClose?.onClick}>
<div
onClick={(e) => e.stopPropagation()}
diff --git a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
index 7d3cf3f57..3f22e4849 100644
--- a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
+++ b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
@@ -20,10 +20,10 @@ import { Button } from "../mui/Button.js";
import arrowDown from "../svg/chevron-down.inline.svg";
import { ParagraphClickable } from "./styled/index.js";
-export interface Props {
- label: (s: string) => TranslatedString;
- actions: string[];
- onClick: (s: string) => Promise<void>;
+export interface Props<T> {
+ label: (s: T) => TranslatedString;
+ actions: T[];
+ onClick: (s: T) => Promise<void>;
}
/**
@@ -37,19 +37,19 @@ export interface Props {
*
* @returns
*/
-export function MultiActionButton({
+export function MultiActionButton<T>({
label,
actions,
onClick: doClick,
-}: Props): VNode {
- const defaultAction = actions.length > 0 ? actions[0] : "";
+}: Props<T>): VNode {
+ const defaultAction = actions.length > 0 ? actions[0] : "" as T;
const [opened, setOpened] = useState(false);
- const [selected, setSelected] = useState<string>(defaultAction);
+ const [selected, setSelected] = useState<T>(defaultAction);
const canChange = actions.length > 1;
const options = canChange ? actions.filter((a) => a !== selected) : [];
- function select(m: string): void {
+ function select(m: T): void {
setSelected(m);
setOpened(false);
}
diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx b/packages/taler-wallet-webextension/src/components/SelectList.tsx
index 6eb72a266..a879da840 100644
--- a/packages/taler-wallet-webextension/src/components/SelectList.tsx
+++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx
@@ -47,7 +47,7 @@ export function SelectList({
<Fragment>
<label
htmlFor={`text-${name}`}
- style={{ marginLeft: "0.5em", fontWeight: "bold" }}
+ style={{ marginLeft: "0.2rem", fontWeight: "bold" }}
>
{" "}
{label}
diff --git a/packages/taler-wallet-webextension/src/components/ShowBanksForPaytoPopup.tsx b/packages/taler-wallet-webextension/src/components/ShowBanksForPaytoPopup.tsx
new file mode 100644
index 000000000..268dcc1b3
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/ShowBanksForPaytoPopup.tsx
@@ -0,0 +1,61 @@
+/*
+ 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 {
+ BankingChoiceSpec
+} from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { ButtonHandler } from "../mui/handlers.js";
+import { Modal } from "./Modal.js";
+
+const BanksTable = styled.table`
+ width: 100%;
+ border-spacing: 0px;
+ & > tr > td {
+ padding: 5px;
+ }
+ & > tr > td:nth-child(2n) {
+ text-align: right;
+ overflow-wrap: anywhere;
+ }
+ & > tr:nth-child(2n) {
+ background: #ebebeb;
+ }
+`;
+
+interface Props { banks: BankingChoiceSpec[], onClose: ButtonHandler };
+
+export function ShowBanksForPaytoPopup({ banks, onClose }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Modal title="Supported banks" onClose={onClose}>
+ <div style={{ overflowY: "auto", height: "95%", padding: 5 }}>
+ <BanksTable>
+ {banks.map((b, idx) => {
+
+ return <tr key={idx}>
+ <td>
+ <a href={b.uri}>{b.label}</a>
+ </td>
+ </tr>
+ })}
+ </BanksTable>
+ </div>
+ </Modal>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/components/ShowQRsForPaytoPopup.tsx b/packages/taler-wallet-webextension/src/components/ShowQRsForPaytoPopup.tsx
new file mode 100644
index 000000000..a1d1d0269
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/ShowQRsForPaytoPopup.tsx
@@ -0,0 +1,94 @@
+/*
+ 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 {
+ QrCodeSpec
+} from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { ButtonHandler } from "../mui/handlers.js";
+import { Modal } from "./Modal.js";
+import { QR } from "./QR.js";
+import { useState } from "preact/hooks";
+
+const QRsTable = styled.table`
+ width: 100%;
+ & > tr > td {
+ padding: 5px;
+ }
+ & > tr > td {
+ border-spacing: 0px;
+ border-radius: 4px;
+ border: 1px black solid;
+ }
+ & > tr > td:nth-child(2n) {
+ text-align: right;
+ overflow-wrap: anywhere;
+ }
+`;
+
+const AccordionCss = styled.div`
+& > .accordion {
+ color: #444;
+ cursor: pointer;
+ padding: 8px;
+ font-size: large;
+ width: 100%;
+ text-align: left;
+ border: none;
+ outline: none;
+ transition: 0.4s;
+}
+
+& > .panel {
+ padding: 0 18px;
+ background-color: white;
+ display: none;
+ overflow: hidden;
+}`
+
+interface Props { qrs: QrCodeSpec[], onClose: ButtonHandler };
+
+function Accordion({ section, content }: { section: string, content: string }): VNode {
+ const [opened, setOpened] = useState(false)
+ return <AccordionCss>
+ <button class={opened ? "accordion active" : "accordion"} onClick={() => { setOpened(!opened) }}>{section}</button>
+ <div class="panel" style={{ display: opened ? "block" : "none" }}>
+ <QR text={content} />
+ </div>
+ </AccordionCss>
+}
+
+export function ShowQRsForPaytoPopup({ qrs, onClose }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Modal title="Qrs" onClose={onClose}>
+ <div style={{ overflowY: "auto", height: "95%", padding: 5 }}>
+ <QRsTable>
+ {qrs.map((q, idx) => {
+
+ return <tr key={idx}>
+ <td>
+ <Accordion section={q.type} content={q.qrContent} />
+ </td>
+ </tr>
+ })}
+ </QRsTable>
+ </div>
+ </Modal>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
index f3172a741..8d9d3a499 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
@@ -159,7 +159,18 @@ export function ShowTosContentView({
</section>
)}
{terms.content && (
- <section style={{ justifyContent: "space-around", display: "flex" }}>
+ <section
+ style={{
+ justifyContent: "space-around",
+ display: "flex",
+ position: "relative",
+ resize: "vertical",
+ overflow: "hidden",
+ marginTop: "4px",
+ minHeight: "120px",
+ height: "240px",
+ }}
+ >
{terms.content.type === "xml" &&
(!terms.content.document ? (
<WarningBox>
@@ -186,7 +197,18 @@ export function ShowTosContentView({
</div>
))}
{terms.content.type === "html" && (
- <iframe style={{ width: "100%" }} srcDoc={terms.content.html} />
+ <iframe
+ style={{
+ width: "100%",
+ height: "100%",
+ border: "2px solid #0003",
+ borderRadius: "4px",
+ boxSizing: "border-box",
+ }}
+ src={`data:text/html;utf-8,${encodeURIComponent(
+ terms.content.html,
+ )}`}
+ />
)}
{terms.content.type === "pdf" && (
<a href={terms.content.location.toString()} download="tos.pdf">
diff --git a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
index c0bc5532b..ab3668ca0 100644
--- a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
+++ b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
@@ -180,7 +180,7 @@ function ShowBalanceChange({ events }: MoreInfoPRops): VNode {
<dd>
<a
title={not.hintTransactionId}
- href={Pages.balanceTransaction({ tid: not.hintTransactionId })}
+ href={`#${Pages.balanceTransaction({ tid: not.hintTransactionId })}`}
>
{not.hintTransactionId.substring(0, 10)}
</a>
@@ -257,7 +257,7 @@ function ShowTransactionStateTransition({
<dd>
<a
title={not.transactionId}
- href={Pages.balanceTransaction({ tid: not.transactionId })}
+ href={`#${Pages.balanceTransaction({ tid: not.transactionId })}`}
>
{not.transactionId.substring(0, 10)}
</a>
@@ -312,34 +312,34 @@ function ShowExchangeStateTransition({ events }: MoreInfoPRops): VNode {
<dt>Exchange</dt>
<dd>{not.exchangeBaseUrl}</dd>
{not.oldExchangeState &&
- not.newExchangeState.exchangeEntryStatus !==
+ not.newExchangeState?.exchangeEntryStatus !==
not.oldExchangeState?.exchangeEntryStatus && (
<Fragment>
<dt>Entry status</dt>
<dd>
from {not.oldExchangeState.exchangeEntryStatus} to{" "}
- {not.newExchangeState.exchangeEntryStatus}
+ {not.newExchangeState?.exchangeEntryStatus}
</dd>
</Fragment>
)}
{not.oldExchangeState &&
- not.newExchangeState.exchangeUpdateStatus !==
+ not.newExchangeState?.exchangeUpdateStatus !==
not.oldExchangeState?.exchangeUpdateStatus && (
<Fragment>
<dt>Update status</dt>
<dd>
from {not.oldExchangeState.exchangeUpdateStatus} to{" "}
- {not.newExchangeState.exchangeUpdateStatus}
+ {not.newExchangeState?.exchangeUpdateStatus}
</dd>
</Fragment>
)}
{not.oldExchangeState &&
- not.newExchangeState.tosStatus !== not.oldExchangeState?.tosStatus && (
+ not.newExchangeState?.tosStatus !== not.oldExchangeState?.tosStatus && (
<Fragment>
<dt>Tos status</dt>
<dd>
from {not.oldExchangeState.tosStatus} to{" "}
- {not.newExchangeState.tosStatus}
+ {not.newExchangeState?.tosStatus}
</dd>
</Fragment>
)}
@@ -722,10 +722,15 @@ function refresh(
let currentTab: chrome.tabs.Tab | undefined;
// Allow running outside the extension for testing
// tslint:disable-next-line:no-string-literal
-if (typeof chrome !== "undefined") {
- chrome.tabs.getCurrent().then((d) => {
- currentTab = d;
- });
+if (typeof chrome !== "undefined" && typeof chrome.tabs !== "undefined") {
+ const p = chrome.tabs.getCurrent();
+ // this may be called outside the render phase (in the background)
+ // when this happen currentTab is not needed but also undefined
+ if (p) {
+ p.then((d) => {
+ currentTab = d;
+ });
+ }
}
export function ObservabilityEventsTable(): VNode {
@@ -1064,7 +1069,9 @@ export function ActiveTasksTable(): VNode {
{task.transaction ? (
<a
title={task.transaction}
- href={Pages.balanceTransaction({ tid: task.transaction })}
+ href={`#${Pages.balanceTransaction({
+ tid: task.transaction,
+ })}`}
>
{task.transaction.substring(0, 10)}
</a>
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index 739b71064..329d1b6e4 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -273,8 +273,15 @@ const Tooltip = styled.div<{ content: string }>`
position: absolute;
z-index: 1000001;
padding: 0.5em 0.75em;
- font: normal normal 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI",
- Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
+ font:
+ normal normal 11px/1.5 -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Helvetica,
+ Arial,
+ sans-serif,
+ "Apple Color Emoji",
+ "Segoe UI Emoji";
-webkit-font-smoothing: subpixel-antialiased;
color: white;
text-align: center;
@@ -539,7 +546,7 @@ export const LinkPrimary = styled(Link)`
color: black;
`;
-export const ButtonPrimary = styled(ButtonVariant) <{ small?: boolean }>`
+export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>`
font-size: ${({ small }: any) => (small ? "small" : "inherit")};
background-color: #0042b2;
border-color: #0042b2;
@@ -690,13 +697,13 @@ export const SmallBoldText = styled.div`
font-weight: bold;
`;
-export const AgeSign = styled.div<{size:number}>`
+export const AgeSign = styled.div<{ size: number }>`
display: inline-block;
border: red solid 1px;
border-radius: 100%;
- width: ${({ size }: {size:number}) => (`${size}px`)};
- height: ${({ size }: {size:number}) => (`${size}px`)};
- line-height: ${({ size }: {size:number}) => (`${size}px`)};
+ width: ${({ size }: { size: number }) => `${size}px`};
+ height: ${({ size }: { size: number }) => `${size}px`};
+ line-height: ${({ size }: { size: number }) => `${size}px`};
padding: 3px;
`;
@@ -920,11 +927,18 @@ export const NiceSelect = styled.div`
background-color: white;
+ border: 2px solid #0003;
border-radius: 0.25rem;
font-size: 1em;
padding: 8px 32px 8px 8px;
/* 0.5em 3em 0.5em 1em; */
cursor: pointer;
+
+ &:hover,
+ &:focus,
+ &:active {
+ background-color: #0000000a;
+ }
}
position: relative;
@@ -1074,7 +1088,9 @@ export const StyledCheckboxLabel = styled.div`
color: #959495;
}
input:focus + div + label {
- box-shadow: 0 0 0 0.05em #fff, 0 0 0.15em 0.1em currentColor;
+ box-shadow:
+ 0 0 0 0.05em #fff,
+ 0 0 0.15em 0.1em currentColor;
}
`;
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
index 6b228188b..d6a14f3dc 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
@@ -14,18 +14,18 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson, AmountString } from "@gnu-taler/taler-util";
+import { AmountJson, AmountString, PaytoUri, ScopeInfo } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
-import { ButtonHandler } from "../../mui/handlers.js";
+import { AmountFieldHandler, ButtonHandler } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
export interface Props {
- talerDepositUri: string | undefined;
- amountStr: AmountString | undefined;
+ account: PaytoUri;
+ scope: ScopeInfo;
cancel: () => Promise<void>;
onSuccess: (tx: string) => Promise<void>;
}
@@ -44,7 +44,9 @@ export namespace State {
export interface Ready {
status: "ready";
error: undefined;
+ amount: AmountFieldHandler;
fee: AmountJson;
+ account: PaytoUri;
cost: AmountJson;
effective: AmountJson;
confirm: ButtonHandler;
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
index efcef8c28..f7a52b7dc 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
@@ -14,52 +14,54 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js";
+import { useState } from "preact/hooks";
export function useComponentState({
- talerDepositUri,
- amountStr,
+ account,
+ scope,
cancel,
onSuccess,
}: Props): State {
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
- const info = useAsyncAsHook(async () => {
- if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
- if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");
- const amount = Amounts.parse(amountStr);
- if (!amount) throw Error("ERROR_INVALID-AMOUNT-FOR-DEPOSIT");
- const deposit = await api.wallet.call(WalletApiOperation.PrepareDeposit, {
- amount: Amounts.stringify(amount),
- depositPaytoUri: talerDepositUri,
+ const [amount, setAmount] = useState(Amounts.zeroOfCurrency(scope.currency))
+ const amountStr = Amounts.stringify(amount);
+
+ const hook = useAsyncAsHook(async () => {
+ const deposit = await api.wallet.call(WalletApiOperation.CheckDeposit, {
+ amount: amountStr,
+ depositPaytoUri: stringifyPaytoUri(account),
});
- return { deposit, uri: talerDepositUri, amount };
- });
- const { i18n } = useTranslationContext();
+ return deposit;
+ }, [amountStr]);
+ // const { i18n } = useTranslationContext();
- if (!info) return { status: "loading", error: undefined };
- if (info.hasError) {
- return {
- status: "error",
- error: alertFromError(
- i18n,
- i18n.str`Could not load the status of deposit`,
- info,
- ),
- };
- }
+ // if (!hook) return { status: "loading", error: undefined };
+ // if (hook.hasError) {
+ // return {
+ // status: "error",
+ // error: alertFromError(
+ // i18n,
+ // i18n.str`Could not load the status of deposit`,
+ // hook,
+ // ),
+ // };
+ // }
- const { deposit, uri, amount } = info.response;
+ const debitAmount = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.effectiveDepositAmount);
+ const toBeReceived = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.totalDepositCost);
+ // const { deposit, uri, amount } = hook.response;
async function doDeposit(): Promise<void> {
const resp = await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
- amount: Amounts.stringify(amount),
- depositPaytoUri: uri,
+ amount: amountStr,
+ depositPaytoUri: stringifyPaytoUri(account),
});
onSuccess(resp.transactionId);
}
@@ -67,13 +69,19 @@ export function useComponentState({
return {
status: "ready",
error: undefined,
+ account,
+ amount: {
+ value: amount,
+ onInput: pushAlertOnError(async (e) => setAmount(e)),
+ error: Amounts.isZero(amount) ? "Can't be zero" : undefined,
+ },
confirm: {
onClick: pushAlertOnError(doDeposit),
},
- fee: Amounts.sub(deposit.totalDepositCost, deposit.effectiveDepositAmount)
+ fee: Amounts.sub(toBeReceived, debitAmount)
.amount,
- cost: Amounts.parseOrThrow(deposit.totalDepositCost),
- effective: Amounts.parseOrThrow(deposit.effectiveDepositAmount),
+ cost: Amounts.parseOrThrow(toBeReceived),
+ effective: Amounts.parseOrThrow(debitAmount),
cancel,
};
}
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
index cd65ce8e1..aacb7b03d 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
import * as tests from "@gnu-taler/web-util/testing";
import { ReadyView } from "./views.js";
@@ -30,6 +30,10 @@ export default {
export const Ready = tests.createExample(ReadyView, {
status: "ready",
confirm: {},
+ amount: {
+ value: Amounts.parseOrThrow("EUR:1")
+ },
+ account: parsePaytoUri("payto://iban/DE1231231231")!,
cost: Amounts.parseOrThrow("EUR:1.2"),
effective: Amounts.parseOrThrow("EUR:1"),
fee: Amounts.parseOrThrow("EUR:0.2"),
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
index 100929918..743bd43e5 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AmountString, Amounts } from "@gnu-taler/taler-util";
+import { AmountString, Amounts, ScopeType, parsePayUri, parsePaytoUri } from "@gnu-taler/taler-util";
import { expect } from "chai";
import { createWalletApiMock } from "../../test-utils.js";
import { useComponentState } from "./state.js";
@@ -28,63 +28,30 @@ import { Props } from "./index.js";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
describe("Deposit CTA states", () => {
- it("should tell the user that the URI is missing", async () => {
- const { handler, TestingContext } = createWalletApiMock();
-
- const props: Props = {
- talerDepositUri: undefined,
- amountStr: undefined,
- cancel: async () => {
- null;
- },
- onSuccess: async () => {
- null;
- },
- };
-
- const hookBehavior = await tests.hookBehaveLikeThis(
- useComponentState,
- props,
- [
- ({ status }) => {
- expect(status).equals("loading");
- },
- ({ status, error }) => {
- expect(status).equals("error");
-
- if (!error) expect.fail();
- // if (!error.hasError) expect.fail();
- // if (error.operational) expect.fail();
- expect(error.description).eq("ERROR_NO-URI-FOR-DEPOSIT");
- },
- ],
- TestingContext,
- );
-
- expect(hookBehavior).deep.equal({ result: "ok" });
- expect(handler.getCallingQueueState()).eq("empty");
- });
it("should be ready after loading", async () => {
const { handler, TestingContext } = createWalletApiMock();
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.CheckDeposit,
undefined,
{
- effectiveDepositAmount: "EUR:1" as AmountString,
- totalDepositCost: "EUR:1.2" as AmountString,
+ effectiveDepositAmount: "EUR:0" as AmountString,
+ totalDepositCost: "EUR:0" as AmountString,
fees: {
coin: "EUR:0" as AmountString,
- refresh: "EUR:0.2" as AmountString,
+ refresh: "EUR:0" as AmountString,
wire: "EUR:0" as AmountString,
},
},
);
const props = {
- talerDepositUri: "payto://refund/asdasdas",
- amountStr: "EUR:1" as AmountString,
+ account: parsePaytoUri("payto://refund/asdasdas")!,
+ scope: {
+ type: ScopeType.Global as const,
+ currency: "EUR",
+ },
cancel: async () => {
null;
},
@@ -97,16 +64,26 @@ describe("Deposit CTA states", () => {
useComponentState,
props,
[
- ({ status }) => {
- expect(status).equals("loading");
+ // ({ status }) => {
+ // expect(status).equals("loading");
+ // },
+ (state) => {
+ if (state.status !== "ready") expect.fail();
+ if (state.error) expect.fail();
+ expect(state.confirm.onClick).not.undefined;
+ expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:0"));
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.confirm.onClick).not.undefined;
- expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
- expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
- expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
+ expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0"));
+ expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:0"));
},
],
TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx b/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
index c683a755c..3e76b4789 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
@@ -14,12 +14,13 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { Amount } from "../../components/Amount.js";
-import { Part } from "../../components/Part.js";
+import { AmountField } from "../../components/AmountField.js";
+import { Part, PartPayto } from "../../components/Part.js";
import { Button } from "../../mui/Button.js";
+import { DepositDetails, getAmountWithFee } from "../../wallet/Transaction.js";
import { State } from "./index.js";
/**
@@ -32,29 +33,24 @@ export function ReadyView(state: State.Ready): VNode {
return (
<Fragment>
- <section>
- {Amounts.isNonZero(state.cost) && (
- <Part
- big
- title={i18n.str`Cost`}
- text={<Amount value={state.cost} />}
- kind="negative"
- />
- )}
- {Amounts.isNonZero(state.fee) && (
- <Part
- big
- title={i18n.str`Fee`}
- text={<Amount value={state.fee} />}
- kind="negative"
+ <section style={{ textAlign: "left" }}>
+ <p>
+ <AmountField
+ label={i18n.str`Amount`}
+ handler={state.amount}
+ required
/>
- )}
+ </p>
+ <PartPayto kind="neutral" payto={state.account} />
<Part
- big
- title={i18n.str`To be received`}
- text={<Amount value={state.effective} />}
- kind="positive"
+ title={i18n.str`Details`}
+ text={
+ <DepositDetails
+ amount={getAmountWithFee(state.cost, state.effective, "debit")}
+ />
+ }
/>
+
</section>
<section>
<Button
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
index 1ca7481be..dbca08b2b 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
@@ -14,12 +14,12 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson, AmountString } from "@gnu-taler/taler-util";
+import { AmountJson, AmountString, ScopeInfo } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
-import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
+import { AmountFieldHandler, ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js";
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
@@ -27,7 +27,7 @@ import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
export interface Props {
- amount: AmountString;
+ scope: ScopeInfo;
onClose: () => Promise<void>;
onSuccess: (tx: string) => Promise<void>;
}
@@ -59,6 +59,7 @@ export namespace State {
status: "ready";
doSelectExchange: ButtonHandler;
create: ButtonHandler;
+ amount: AmountFieldHandler;
subject: TextFieldHandler;
expiration: TextFieldHandler;
toBeReceived: AmountJson;
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index baaa9a3dd..d2db4f44c 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -15,7 +15,7 @@
*/
/* eslint-disable react-hooks/rules-of-hooks */
-import { Amounts, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, AmountString, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { isFuture, parse } from "date-fns";
import { useState } from "preact/hooks";
@@ -28,11 +28,10 @@ import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";
export function useComponentState({
- amount: amountStr,
+ scope,
onClose,
onSuccess,
}: Props): RecursiveState<State> {
- const amount = Amounts.parseOrThrow(amountStr);
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
@@ -72,12 +71,14 @@ export function useComponentState({
const exchangeList = hook.response.exchanges;
return () => {
+ const [amount, setAmount] = useState<AmountJson>(Amounts.zeroOfCurrency(scope.currency));
const [subject, setSubject] = useState<string | undefined>();
const [timestamp, setTimestamp] = useState<string | undefined>();
const { pushAlertOnError } = useAlertContext();
+ const amountStr = Amounts.stringify(amount)
const selectedExchange = useSelectedExchange({
- currency: amount.currency,
+ currency: scope.currency,
defaultExchange: undefined,
list: exchangeList,
});
@@ -97,39 +98,41 @@ export function useComponentState({
},
);
return resp;
- });
-
- if (!hook) {
- return {
- status: "loading",
- error: undefined,
- };
- }
-
- if (hook.hasError) {
- return {
- status: "error",
- retry: {
- onClick: pushAlertOnError(async () => {
- hook.retry();
- }),
- },
- error: alertFromError(
- i18n,
- i18n.str`Could not load the invoice status`,
- hook,
- ),
- };
- // return {
- // status: "loading-uri",
- // error: hook,
- // };
- }
-
- const { amountEffective, amountRaw } = hook.response;
- const requestAmount = Amounts.parseOrThrow(amountRaw);
- const toBeReceived = Amounts.parseOrThrow(amountEffective);
-
+ },[amountStr]);
+
+ // if (!hook) {
+ // return {
+ // status: "loading",
+ // error: undefined,
+ // };
+ // }
+
+ // if (hook.hasError) {
+ // return {
+ // status: "error",
+ // retry: {
+ // onClick: pushAlertOnError(async () => {
+ // hook.retry();
+ // }),
+ // },
+ // error: alertFromError(
+ // i18n,
+ // i18n.str`Could not load the invoice status`,
+ // hook,
+ // ),
+ // };
+ // // return {
+ // // status: "loading-uri",
+ // // error: hook,
+ // // };
+ // }
+
+ // const { amountEffective, amountRaw } = hook.response;
+ // const requestAmount = Amounts.parseOrThrow(amountRaw);
+ // const toBeReceived = Amounts.parseOrThrow(amountEffective);
+ const requestAmount = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.amountRaw);
+ const toBeReceived = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.amountEffective);
+
let purse_expiration: TalerProtocolTimestamp | undefined = undefined;
let timestampError: string | undefined = undefined;
@@ -174,6 +177,11 @@ export function useComponentState({
return {
status: "ready",
+ amount: {
+ value: amount,
+ onInput: pushAlertOnError(async (e) => setAmount(e)),
+ error: Amounts.isZero(amount) ? "Can't be zero" : undefined,
+ },
subject: {
error:
subject === undefined
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
index 779f130aa..9822f7c91 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
@@ -33,6 +33,13 @@ export const Ready = tests.createExample(ReadyView, {
value: 1,
fraction: 0,
},
+ amount: {
+ value: {
+ currency: "ARS",
+ value: 1,
+ fraction: 0,
+ }
+ },
expiration: {
value: "2/12/12",
},
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
index e2c37fbba..86bac5a16 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
@@ -27,10 +27,12 @@ import {
InvoiceCreationDetails,
} from "../../wallet/Transaction.js";
import { State } from "./index.js";
+import { AmountField } from "../../components/AmountField.js";
export function ReadyView({
exchangeUrl,
subject,
+ amount,
expiration,
create,
toBeReceived,
@@ -87,6 +89,13 @@ export function ReadyView({
big
/>
<p>
+ <AmountField
+ label={i18n.str`Amount`}
+ handler={amount}
+ required
+ />
+ </p>
+ <p>
<TextField
label="Subject"
variant="filled"
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index dcb1f827b..d010ac662 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -17,14 +17,13 @@
import {
AbsoluteTime,
AmountJson,
- PreparePayResult,
- TalerErrorDetail,
+ PreparePayResult
} from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
import { ButtonHandler } from "../../mui/handlers.js";
-import { compose, StateViewMap } from "../../utils/index.js";
+import { StateViewMap, compose } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
index 539ca207c..2969efb7f 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
@@ -17,18 +17,19 @@
import {
AmountJson,
AmountString,
+ ScopeInfo,
TalerErrorDetail,
} from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
-import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
+import { AmountFieldHandler, ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
export interface Props {
- amount: AmountString;
+ scope: ScopeInfo;
onClose: () => Promise<void>;
onSuccess: (tx: string) => Promise<void>;
}
@@ -54,6 +55,7 @@ export namespace State {
export interface Ready extends BaseInfo {
status: "ready";
create: ButtonHandler;
+ amount: AmountFieldHandler;
toBeReceived: AmountJson;
debitAmount: AmountJson;
subject: TextFieldHandler;
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index f15d48c23..1a8f318c0 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -15,64 +15,42 @@
*/
import {
- AmountString,
+ AmountJson,
Amounts,
- TalerErrorCode,
- TalerProtocolTimestamp,
+ TalerProtocolTimestamp
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { isFuture, parse } from "date-fns";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { BackgroundError, WxApiType } from "../../wxApi.js";
import { Props, State } from "./index.js";
export function useComponentState({
- amount: amountStr,
+ scope,
onClose,
onSuccess,
}: Props): State {
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
- const amount = Amounts.parseOrThrow(amountStr);
+ // const amount = Amounts.parseOrThrow(amountStr);
const { i18n } = useTranslationContext();
+ const [amount, setAmount] = useState<AmountJson>(Amounts.zeroOfCurrency(scope.currency));
const [subject, setSubject] = useState<string | undefined>();
const [timestamp, setTimestamp] = useState<string | undefined>();
+ const amountStr = Amounts.stringify(amount)
const hook = useAsyncAsHook(async () => {
- const resp = await checkPeerPushDebitAndCheckMax(api, amountStr);
- return resp;
- });
-
- if (!hook) {
- return {
- status: "loading",
- error: undefined,
- };
- }
- if (hook.hasError) {
- return {
- status: "error",
- retry: {
- onClick: pushAlertOnError(async () => {
- hook.retry();
- }),
- },
- error: alertFromError(
- i18n,
- i18n.str`Could not load the max amount to transfer`,
- hook,
- ),
- };
- }
+ return await api.wallet.call(WalletApiOperation.CheckPeerPushDebit, {
+ amount: amountStr,
+ });
+ }, [amountStr]);
- const { amountEffective, amountRaw } = hook.response;
- const debitAmount = Amounts.parseOrThrow(amountEffective);
- const toBeReceived = Amounts.parseOrThrow(amountRaw);
+ const debitAmount = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.amountEffective);
+ const toBeReceived = !hook || hook.hasError ? Amounts.zeroOfCurrency(scope.currency) : Amounts.parseOrThrow(hook.response.amountRaw);
let purse_expiration: TalerProtocolTimestamp | undefined = undefined;
let timestampError: string | undefined = undefined;
@@ -112,13 +90,18 @@ export function useComponentState({
}
const unableToCreate =
- !subject || Amounts.isZero(amount) || !purse_expiration;
+ !subject || Amounts.isZero(amount) || Amounts.isZero(debitAmount) || !purse_expiration;
return {
status: "ready",
cancel: {
onClick: pushAlertOnError(onClose),
},
+ amount: {
+ value: amount,
+ onInput: pushAlertOnError(async (e) => setAmount(e)),
+ error: Amounts.isZero(amount) ? "Can't be zero" : undefined,
+ },
subject: {
error:
subject === undefined
@@ -145,46 +128,3 @@ export function useComponentState({
};
}
-async function checkPeerPushDebitAndCheckMax(
- api: WxApiType,
- amountState: AmountString,
-) {
- // FIXME : https://bugs.gnunet.org/view.php?id=7872
- try {
- return await api.wallet.call(WalletApiOperation.CheckPeerPushDebit, {
- amount: amountState,
- });
- } catch (e) {
- if (!(e instanceof BackgroundError)) {
- throw e;
- }
- if (
- !e.hasErrorCode(
- TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
- )
- ) {
- throw e;
- }
- const material = Amounts.parseOrThrow(
- e.errorDetail.insufficientBalanceDetails.balanceMaterial,
- );
- const amount = Amounts.parseOrThrow(amountState);
- const gap = Amounts.sub(
- amount,
- Amounts.parseOrThrow(
- e.errorDetail.insufficientBalanceDetails.maxEffectiveSpendAmount,
- ),
- ).amount;
- const newAmount = Amounts.sub(material, gap).amount;
- if (Amounts.cmp(newAmount, amount) === 0) {
- //insufficient balance and the exception didn't give
- //a good response that allow us to try again
- throw e;
- }
- if (Amounts.cmp(newAmount, amount) === 1) {
- //how can this happen?
- throw e;
- }
- return checkPeerPushDebitAndCheckMax(api, Amounts.stringify(newAmount));
- }
-}
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
index 8e9fbbe63..9ba806ba4 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
@@ -33,6 +33,13 @@ export const Ready = tests.createExample(ReadyView, {
value: 1,
fraction: 0,
},
+ amount: {
+ value: {
+ currency: "ARS",
+ value: 1,
+ fraction: 0,
+ }
+ },
expiration: {
value: "20/1/2022",
},
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
index bc855f33d..269f39fbe 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
@@ -25,9 +25,11 @@ import {
TransferCreationDetails,
} from "../../wallet/Transaction.js";
import { State } from "./index.js";
+import { AmountField } from "../../components/AmountField.js";
export function ReadyView({
subject,
+ amount,
expiration,
toBeReceived,
debitAmount,
@@ -61,6 +63,13 @@ export function ReadyView({
<Fragment>
<section style={{ textAlign: "left" }}>
<p>
+ <AmountField
+ label={i18n.str`Amount`}
+ handler={amount}
+ required
+ />
+ </p>
+ <p>
<TextField
label="Subject"
variant="filled"
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index 418fef505..8d4ad0bde 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -16,9 +16,9 @@
import {
AmountJson,
- AmountString,
CurrencySpecification,
ExchangeListItem,
+ ScopeInfo
} from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js";
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
@@ -37,7 +37,7 @@ import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { ErrorAlert } from "../../context/alert.js";
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
-import { FinalStateOperation, SelectAmountView, SuccessView } from "./views.js";
+import { FinalStateOperation, SuccessView } from "./views.js";
export interface PropsFromURI {
talerWithdrawUri: string | undefined;
@@ -47,10 +47,10 @@ export interface PropsFromURI {
export interface PropsFromParams {
talerExchangeWithdrawUri: string | undefined;
- amount: string | undefined;
+ scope: ScopeInfo | undefined;
+ amount: AmountJson | undefined;
cancel: () => Promise<void>;
onSuccess: (txid: string) => Promise<void>;
- onAmountChanged: (amount: AmountString) => Promise<void>;
}
export type State =
@@ -58,7 +58,7 @@ export type State =
| State.LoadingUriError
| SelectExchangeState.NoExchangeFound
| SelectExchangeState.Selecting
- | State.SelectAmount
+ // | State.SelectAmount
| State.AlreadyCompleted
| State.Success;
@@ -72,14 +72,14 @@ export namespace State {
error: ErrorAlert;
}
- export interface SelectAmount {
- status: "select-amount";
- error: undefined;
- exchangeBaseUrl: string;
- confirm: ButtonHandler;
- amount: AmountFieldHandler;
- currency: string;
- }
+ // export interface SelectAmount {
+ // status: "select-amount";
+ // error: undefined;
+ // exchangeBaseUrl: string;
+ // confirm: ButtonHandler;
+ // amount: AmountFieldHandler;
+ // currency: string;
+ // }
export interface AlreadyCompleted {
status: "already-completed";
operationState: "confirmed" | "aborted" | "selected";
@@ -126,7 +126,7 @@ export namespace State {
const viewMapping: StateViewMap<State> = {
loading: Loading,
error: ErrorAlertView,
- "select-amount": SelectAmountView,
+ // "select-amount": SelectAmountView,
"no-exchange-found": NoExchangesView,
"selecting-exchange": ExchangeSelectionPage,
success: SuccessView,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 0541bbf3f..232bfe9e2 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -22,6 +22,7 @@ import {
ExchangeListItem,
NotificationType,
TransactionMajorState,
+ TransactionMinorState,
parseWithdrawExchangeUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -36,14 +37,14 @@ import { PropsFromParams, PropsFromURI, State } from "./index.js";
export function useComponentStateFromParams({
talerExchangeWithdrawUri: maybeTalerUri,
- amount,
+ scope,
+ amount: paramsAmount,
cancel,
- onAmountChanged,
onSuccess,
}: PropsFromParams): RecursiveState<State> {
const api = useBackendContext();
const { i18n } = useTranslationContext();
- const paramsAmount = amount ? Amounts.parse(amount) : undefined;
+ // const paramsAmount = amount ? Amounts.parse(amount) : undefined;
const [updatedExchangeByUser, setUpdatedExchangeByUser] = useState<string>();
const uriInfoHook = useAsyncAsHook(async () => {
const exchanges = await api.wallet.call(
@@ -74,6 +75,10 @@ export function useComponentStateFromParams({
return { amount: chosenAmount, exchanges, exchange: ex };
});
+ useEffect(() => {
+ uriInfoHook?.retry();
+ }, [paramsAmount]);
+
if (!uriInfoHook) return { status: "loading", error: undefined };
if (uriInfoHook.hasError) {
@@ -87,75 +92,83 @@ export function useComponentStateFromParams({
};
}
- useEffect(() => {
- uriInfoHook?.retry();
- }, [amount]);
+ const currency = uriInfoHook.response.exchange?.currency ?? scope?.currency;
const exchangeByTalerUri = uriInfoHook.response.exchange?.exchangeBaseUrl;
const exchangeList = uriInfoHook.response.exchanges.exchanges;
const maybeAmount = uriInfoHook.response.amount ?? paramsAmount;
-
- if (!maybeAmount) {
- const exchangeBaseUrl =
- uriInfoHook.response.exchange?.exchangeBaseUrl ??
- (exchangeList.length > 0 ? exchangeList[0].exchangeBaseUrl : undefined);
- const currency =
- uriInfoHook.response.exchange?.currency ??
- (exchangeList.length > 0 ? exchangeList[0].currency : undefined);
-
- if (!exchangeBaseUrl) {
- return {
- status: "error",
- error: {
- message: i18n.str`Can't withdraw from exchange`,
- description: i18n.str`Missing base URL`,
- cause: undefined,
- context: {},
- type: "error",
- },
- };
- }
- if (!currency) {
- return {
- status: "error",
- error: {
- message: i18n.str`Can't withdraw from exchange`,
- description: i18n.str`Missing unknown currency`,
- cause: undefined,
- context: {},
- type: "error",
- },
- };
- }
- return () => {
- const { pushAlertOnError } = useAlertContext();
- const [amount, setAmount] = useState<AmountJson>(
- Amounts.zeroOfCurrency(currency),
- );
- const isValid = Amounts.isNonZero(amount);
- return {
- status: "select-amount",
- currency,
- exchangeBaseUrl,
- error: undefined,
- confirm: {
- onClick: isValid
- ? pushAlertOnError(async () => {
- onAmountChanged(Amounts.stringify(amount));
- })
- : undefined,
- },
- amount: {
- value: amount,
- onInput: pushAlertOnError(async (e) => {
- setAmount(e);
- }),
- },
- };
+ if (!currency) {
+ return {
+ status: "error",
+ error: alertFromError(
+ i18n,
+ i18n.str`Could not load the list of exchanges`,
+ {} as any,
+ ),
};
}
- const chosenAmount = maybeAmount;
+
+ // if (!maybeAmount) {
+ // const exchangeBaseUrl =
+ // uriInfoHook.response.exchange?.exchangeBaseUrl ??
+ // (exchangeList.length > 0 ? exchangeList[0].exchangeBaseUrl : undefined);
+ // const currency =
+ // uriInfoHook.response.exchange?.currency ??
+ // (exchangeList.length > 0 ? exchangeList[0].currency : undefined);
+
+ // if (!exchangeBaseUrl) {
+ // return {
+ // status: "error",
+ // error: {
+ // message: i18n.str`Can't withdraw from exchange`,
+ // description: i18n.str`Missing base URL`,
+ // cause: undefined,
+ // context: {},
+ // type: "error",
+ // },
+ // };
+ // }
+ // if (!currency) {
+ // return {
+ // status: "error",
+ // error: {
+ // message: i18n.str`Can't withdraw from exchange`,
+ // description: i18n.str`Missing unknown currency`,
+ // cause: undefined,
+ // context: {},
+ // type: "error",
+ // },
+ // };
+ // }
+ // return () => {
+ // const { pushAlertOnError } = useAlertContext();
+ // const [amount, setAmount] = useState<AmountJson>(
+ // Amounts.zeroOfCurrency(currency),
+ // );
+ // const isValid = Amounts.isNonZero(amount);
+ // return {
+ // status: "select-amount",
+ // currency,
+ // exchangeBaseUrl,
+ // error: undefined,
+ // confirm: {
+ // onClick: isValid
+ // ? pushAlertOnError(async () => {
+ // onAmountChanged(Amounts.stringify(amount));
+ // })
+ // : undefined,
+ // },
+ // amount: {
+ // value: amount,
+ // onInput: pushAlertOnError(async (e) => {
+ // setAmount(e);
+ // }),
+ // },
+ // };
+ // };
+ // }
+ const chosenAmount = maybeAmount ?? Amounts.zeroOfCurrency(currency);
async function doManualWithdraw(
exchange: string,
@@ -207,6 +220,7 @@ export function useComponentStateFromURI({
const api = useBackendContext();
const { i18n } = useTranslationContext();
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [updatedExchangeByUser, setUpdatedExchangeByUser] = useState<string>();
/**
* Ask the wallet about the withdraw URI
@@ -315,11 +329,14 @@ export function useComponentStateFromURI({
uriInfoHook.response.status !== "pending"
) {
const info = uriInfoHook.response.txInfo;
+ const anotherWallet =
+ info.txState.major === TransactionMajorState.Aborted &&
+ info.txState.minor === TransactionMinorState.CompletedByOtherWallet;
return {
status: "already-completed",
operationState: uriInfoHook.response.status,
confirmTransferUrl: bwi.confirmTransferUrl,
- thisWallet: info.txState.major === TransactionMajorState.Pending,
+ thisWallet: !anotherWallet,
redirectToTx: () => onSuccess(info.transactionId),
error: undefined,
};
@@ -508,10 +525,7 @@ function exchangeSelectionState(
const altCurrencies = amountHook.response.accounts
.filter((a) => !!a.currencySpecification)
.map((a) => a.currencySpecification!.name);
- const chooseCurrencies =
- altCurrencies.length === 0
- ? []
- : [toBeReceived.currency, ...altCurrencies];
+ const chooseCurrencies = altCurrencies.length <= 1 ? [] : altCurrencies;
const convAccount = amountHook.response.accounts.find((c) => {
return (
@@ -528,8 +542,11 @@ function exchangeSelectionState(
const amountError = Amounts.isZero(choosenAmount)
? i18n.str`should be greater than zero`
- : Amounts.cmp(choosenAmount, wInfo.maxAmount) === -1
- ? i18n.str`choose a lower value`
+ : Amounts.isNonZero(wInfo.maxAmount) &&
+ Amounts.cmp(choosenAmount, wInfo.maxAmount) === 1
+ ? i18n.str`can't be greater than ${Amounts.stringifyValue(
+ wInfo.maxAmount,
+ )}`
: undefined;
return {
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index b6a356de8..3283f998f 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -39,10 +39,10 @@ import {
getAmountWithFee,
} from "../../wallet/Transaction.js";
import { State } from "./index.js";
-import { Amounts } from "@gnu-taler/taler-util";
export function FinalStateOperation(state: State.AlreadyCompleted): VNode {
const { i18n } = useTranslationContext();
+
// document.location.href = res.confirmTransferUrl
if (state.thisWallet) {
switch (state.operationState) {
@@ -280,44 +280,3 @@ function WithdrawWithMobile({
);
}
-export function SelectAmountView({
- amount,
- exchangeBaseUrl,
- confirm,
-}: State.SelectAmount): VNode {
- const { i18n } = useTranslationContext();
- return (
- <Fragment>
- <section style={{ textAlign: "left" }}>
- <Part
- title={
- <div
- style={{
- display: "flex",
- alignItems: "center",
- }}
- >
- <i18n.Translate>Exchange</i18n.Translate>
- </div>
- }
- text={<ExchangeDetails exchange={exchangeBaseUrl} />}
- kind="neutral"
- big
- />
- <Grid container columns={2} justifyContent="space-between">
- <AmountField label={i18n.str`Amount`} required handler={amount} />
- </Grid>
- </section>
- <section>
- <Button
- variant="contained"
- color="info"
- disabled={!confirm.onClick}
- onClick={confirm.onClick}
- >
- <i18n.Translate>See details</i18n.Translate>
- </Button>
- </section>
- </Fragment>
- );
-}
diff --git a/packages/taler-wallet-webextension/src/cta/termsExample.ts b/packages/taler-wallet-webextension/src/cta/termsExample.ts
index ba0bee89e..0e5ff6d4b 100644
--- a/packages/taler-wallet-webextension/src/cta/termsExample.ts
+++ b/packages/taler-wallet-webextension/src/cta/termsExample.ts
@@ -21,17 +21,18 @@
*/
export const termsHtml = `<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Terms Of Service &#8212; Taler Terms of Service</title>
-</head><body>
- <div>
- Terms of service
- </div>
- <div>
- A complete separated html with it's own design
- </div>
-</body>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Terms Of Service &#8212; Taler Terms of Service</title>
+ </head>
+ <body>
+ <div>
+ Terms of service
+ </div>
+ <div>
+ A complete separated html with it's own design
+ </div>
+ </body>
</html>
`;
export const termsPlain = `
diff --git a/packages/taler-wallet-webextension/src/i18n/de.po b/packages/taler-wallet-webextension/src/i18n/de.po
index e54c3067d..6d0b5139c 100644
--- a/packages/taler-wallet-webextension/src/i18n/de.po
+++ b/packages/taler-wallet-webextension/src/i18n/de.po
@@ -17,7 +17,7 @@ msgstr ""
"Project-Id-Version: Taler Wallet\n"
"Report-Msgid-Bugs-To: languages@taler.net\n"
"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2024-07-01 22:17+0000\n"
+"PO-Revision-Date: 2024-10-09 11:43+0000\n"
"Last-Translator: Stefan Kügel <stefan.kuegel@taler.net>\n"
"Language-Team: German <https://weblate.taler.net/projects/gnu-taler/"
"webextensions/de/>\n"
@@ -31,7 +31,7 @@ msgstr ""
#: src/NavigationBar.tsx:139
#, c-format
msgid "Balance"
-msgstr "Guthaben"
+msgstr "Saldo"
#: src/NavigationBar.tsx:142
#, c-format
@@ -61,7 +61,7 @@ msgstr "%1$s"
#: src/components/PendingTransactions.tsx:74
#, c-format
msgid "PENDING OPERATIONS"
-msgstr ""
+msgstr "AUSSTEHENDE VORGÄNGE"
#: src/components/Loading.tsx:36
#, c-format
@@ -81,7 +81,7 @@ msgstr ""
#: src/wallet/BackupPage.tsx:205
#, c-format
msgid "Add provider"
-msgstr ""
+msgstr "Anbieter hinzufügen"
#: src/wallet/BackupPage.tsx:219
#, c-format
@@ -91,22 +91,22 @@ msgstr ""
#: src/wallet/BackupPage.tsx:221
#, c-format
msgid "Sync now"
-msgstr ""
+msgstr "Jetzt synchronisieren"
#: src/wallet/BackupPage.tsx:264
#, c-format
msgid "Last synced"
-msgstr ""
+msgstr "Zuletzt synchronisiert"
#: src/wallet/BackupPage.tsx:269
#, c-format
msgid "Not synced"
-msgstr ""
+msgstr "Nicht synchronisiert"
#: src/wallet/BackupPage.tsx:289
#, c-format
msgid "Expires in"
-msgstr ""
+msgstr "Läuft ab in"
#: src/wallet/ProviderDetailPage.tsx:60
#, c-format
@@ -121,32 +121,32 @@ msgstr ""
#: src/wallet/ProviderDetailPage.tsx:115
#, c-format
msgid "See providers"
-msgstr ""
+msgstr "Zahlungsdienste ansehen"
#: src/wallet/ProviderDetailPage.tsx:143
#, c-format
msgid "Last backup"
-msgstr ""
+msgstr "Letztes Backup"
#: src/wallet/ProviderDetailPage.tsx:148
#, c-format
msgid "Back up"
-msgstr ""
+msgstr "Backup ausführen"
#: src/wallet/ProviderDetailPage.tsx:154
#, c-format
msgid "Provider fee"
-msgstr ""
+msgstr "Anbietergebühr"
#: src/wallet/ProviderDetailPage.tsx:157
#, c-format
msgid "per year"
-msgstr ""
+msgstr "pro Jahr"
#: src/wallet/ProviderDetailPage.tsx:163
#, c-format
msgid "Extend"
-msgstr ""
+msgstr "Erweitern"
#: src/wallet/ProviderDetailPage.tsx:169
#, c-format
@@ -160,17 +160,17 @@ msgstr ""
#: src/wallet/ProviderDetailPage.tsx:179
#, c-format
msgid "old"
-msgstr ""
+msgstr "alt"
#: src/wallet/ProviderDetailPage.tsx:183
#, c-format
msgid "new"
-msgstr ""
+msgstr "neu"
#: src/wallet/ProviderDetailPage.tsx:190
#, c-format
msgid "fee"
-msgstr ""
+msgstr "Gebühr"
#: src/wallet/ProviderDetailPage.tsx:198
#, c-format
@@ -180,12 +180,12 @@ msgstr ""
#: src/wallet/ProviderDetailPage.tsx:215
#, c-format
msgid "Remove provider"
-msgstr ""
+msgstr "Anbieter entfernen"
#: src/wallet/ProviderDetailPage.tsx:228
#, c-format
msgid "This provider has reported an error"
-msgstr ""
+msgstr "Dieser Anbieter hat einen Fehler gemeldet"
#: src/wallet/ProviderDetailPage.tsx:242
#, c-format
@@ -215,7 +215,7 @@ msgstr ""
#: src/wallet/AddNewActionView.tsx:57
#, c-format
msgid "Cancel"
-msgstr "Zurück"
+msgstr "Abbrechen"
#: src/wallet/AddNewActionView.tsx:68
#, c-format
@@ -455,7 +455,7 @@ msgstr ""
#: src/components/Part.tsx:163
#, c-format
msgid "IBAN"
-msgstr ""
+msgstr "IBAN"
#: src/cta/Deposit/views.tsx:38
#, fuzzy, c-format
@@ -517,7 +517,7 @@ msgstr ""
#: src/components/BankDetailsByPaytoType.tsx:110
#, c-format
msgid "Account"
-msgstr ""
+msgstr "Konto"
#: src/components/BankDetailsByPaytoType.tsx:116
#, c-format
@@ -532,7 +532,7 @@ msgstr ""
#: src/components/BankDetailsByPaytoType.tsx:148
#, c-format
msgid "Subject"
-msgstr "Verwendungszweck"
+msgstr "Buchungsvermerk"
#: src/components/BankDetailsByPaytoType.tsx:154
#, c-format
@@ -589,7 +589,7 @@ msgstr "Bestätigen"
#: src/wallet/Transaction.tsx:267
#, c-format
msgid "Withdrawal"
-msgstr "Abheben"
+msgstr "Abhebung"
#: src/wallet/Transaction.tsx:286
#, c-format
@@ -719,7 +719,7 @@ msgstr ""
#: src/wallet/Transaction.tsx:635
#, c-format
msgid "Exchange"
-msgstr "Exchange"
+msgstr "Zahlungsdienstleister"
#: src/wallet/Transaction.tsx:641
#, c-format
@@ -737,9 +737,9 @@ msgid "Transfer"
msgstr ""
#: src/wallet/Transaction.tsx:844
-#, fuzzy, c-format
+#, c-format
msgid "Country"
-msgstr "Betrag"
+msgstr "Land"
#: src/wallet/Transaction.tsx:852
#, c-format
@@ -749,42 +749,42 @@ msgstr ""
#: src/wallet/Transaction.tsx:860
#, c-format
msgid "Building number"
-msgstr ""
+msgstr "Zusätzliche Adressangabe"
#: src/wallet/Transaction.tsx:868
#, c-format
msgid "Building name"
-msgstr ""
+msgstr "Bezeichnung des Gebäudes"
#: src/wallet/Transaction.tsx:876
#, c-format
msgid "Street"
-msgstr ""
+msgstr "Str."
#: src/wallet/Transaction.tsx:884
#, c-format
msgid "Post code"
-msgstr ""
+msgstr "Postleitzahl"
#: src/wallet/Transaction.tsx:892
#, c-format
msgid "Town location"
-msgstr ""
+msgstr "Ortsteil"
#: src/wallet/Transaction.tsx:900
#, c-format
msgid "Town"
-msgstr ""
+msgstr "Ort"
#: src/wallet/Transaction.tsx:908
#, c-format
msgid "District"
-msgstr ""
+msgstr "Bezirk/Kanton"
#: src/wallet/Transaction.tsx:916
#, c-format
msgid "Country subdivision"
-msgstr ""
+msgstr "Region"
#: src/wallet/Transaction.tsx:935
#, c-format
@@ -900,6 +900,8 @@ msgstr ""
#, c-format
msgid "You have no balance for this currency. Withdraw digital cash first."
msgstr ""
+"Sie haben kein Guthaben in dieser Währung. Bitte führen Sie zuerst eine "
+"Abhebung durch, um e-Geld dieser Währung zu erhalten."
#: src/cta/Payment/views.tsx:364
#, c-format
@@ -926,7 +928,7 @@ msgstr "Konnte die Umsatzanzeige nicht laden"
#: src/cta/Refund/views.tsx:48
#, c-format
msgid "Digital cash refund"
-msgstr ""
+msgstr "Erstattung von e-Geld"
#: src/cta/Refund/views.tsx:52
#, c-format
@@ -1025,7 +1027,8 @@ msgstr "Ich akzeptiere die Allgemeinen Geschäftsbedingungen (AGB)"
#: src/components/TermsOfService/views.tsx:107
#, c-format
msgid "Exchange doesn&apos;t have terms of service"
-msgstr "Dieser Exchange hat keine Allgemeine Geschäftsbedingungen (AGB)"
+msgstr ""
+"Dieser Zahlungsdienstleister hat keine Allgemeinen Geschäftsbedingungen (AGB)"
#: src/components/TermsOfService/views.tsx:135
#, c-format
@@ -1187,7 +1190,7 @@ msgstr ""
#: src/cta/Withdraw/views.tsx:74
#, c-format
msgid "Digital cash withdrawal"
-msgstr ""
+msgstr "Abhebung von e-Geld"
#: src/cta/Withdraw/views.tsx:79
#, c-format
@@ -1232,7 +1235,7 @@ msgstr ""
#: src/cta/TransferCreate/views.tsx:55
#, c-format
msgid "Digital cash transfer"
-msgstr ""
+msgstr "Übertragung von e-Geld"
#: src/cta/TransferCreate/views.tsx:59
#, c-format
@@ -1365,7 +1368,7 @@ msgstr "Allgemeine Geschäftsbedingungen (AGB) ansehen"
#: src/wallet/ExchangeAddConfirm.tsx:45
#, c-format
msgid "Exchange URL"
-msgstr "URL des Exchange"
+msgstr "URL des Zahlungsdienstleisters (Exchange-URL)"
#: src/wallet/ExchangeAddConfirm.tsx:70
#, c-format
@@ -1415,7 +1418,7 @@ msgstr ""
#: src/wallet/ExchangeSetUrl.tsx:206
#, c-format
msgid "Next"
-msgstr ""
+msgstr "Nächste"
#: src/components/TransactionItem.tsx:201
#, c-format
@@ -1460,7 +1463,7 @@ msgstr ""
#: src/wallet/ProviderAddPage.tsx:158
#, c-format
msgid "Name"
-msgstr ""
+msgstr "Name"
#: src/wallet/ProviderAddPage.tsx:212
#, c-format
@@ -1978,7 +1981,7 @@ msgstr ""
#: src/components/EditableText.tsx:45
#, c-format
msgid "Edit"
-msgstr ""
+msgstr "Bearbeiten"
#: src/wallet/ManualWithdrawPage.tsx:102
#, c-format
diff --git a/packages/taler-wallet-webextension/src/i18n/es.po b/packages/taler-wallet-webextension/src/i18n/es.po
index ea1fa9803..eb43e2949 100644
--- a/packages/taler-wallet-webextension/src/i18n/es.po
+++ b/packages/taler-wallet-webextension/src/i18n/es.po
@@ -17,8 +17,8 @@ msgstr ""
"Project-Id-Version: Taler Wallet\n"
"Report-Msgid-Bugs-To: languages@taler.net\n"
"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2024-03-07 07:03+0000\n"
-"Last-Translator: Javier Sepulveda <javier.sepulveda@uv.es>\n"
+"PO-Revision-Date: 2024-07-03 18:32+0000\n"
+"Last-Translator: Luis Avalos <avalos.diaz.0577@gmail.com>\n"
"Language-Team: Spanish <https://weblate.taler.net/projects/gnu-taler/"
"webextensions/es/>\n"
"Language: es\n"
@@ -26,7 +26,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 5.2.1\n"
+"X-Generator: Weblate 5.5.5\n"
#: src/NavigationBar.tsx:139
#, c-format
@@ -1386,7 +1386,7 @@ msgstr "Revisar los términos de servicio"
#: src/wallet/ExchangeAddConfirm.tsx:45
#, c-format
msgid "Exchange URL"
-msgstr "URL del Exchange"
+msgstr "URL del proveedor"
#: src/wallet/ExchangeAddConfirm.tsx:70
#, c-format
diff --git a/packages/taler-wallet-webextension/src/i18n/tr.po b/packages/taler-wallet-webextension/src/i18n/tr.po
index 5848b9f3a..3af81d0bc 100644
--- a/packages/taler-wallet-webextension/src/i18n/tr.po
+++ b/packages/taler-wallet-webextension/src/i18n/tr.po
@@ -17,8 +17,8 @@ msgstr ""
"Project-Id-Version: Taler Wallet\n"
"Report-Msgid-Bugs-To: languages@taler.net\n"
"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2024-03-08 01:14+0000\n"
-"Last-Translator: Alp <berna.alp@digitalekho.com>\n"
+"PO-Revision-Date: 2024-09-14 05:26+0000\n"
+"Last-Translator: Muha Aliss <muhaaliss@tuta.io>\n"
"Language-Team: Turkish <https://weblate.taler.net/projects/gnu-taler/"
"webextensions/tr/>\n"
"Language: tr\n"
@@ -26,7 +26,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 5.2.1\n"
+"X-Generator: Weblate 5.5.5\n"
#: src/NavigationBar.tsx:139
#, c-format
@@ -41,7 +41,7 @@ msgstr "Yedekle"
#: src/NavigationBar.tsx:147
#, c-format
msgid "QR Reader and Taler URI"
-msgstr ""
+msgstr "QR Okuyucu ve Taler URI'si"
#: src/NavigationBar.tsx:154
#, c-format
@@ -54,14 +54,14 @@ msgid "Dev"
msgstr "Gelişim"
#: src/mui/Typography.tsx:122
-#, fuzzy, c-format
+#, c-format
msgid "%1$s"
msgstr "%1$s"
#: src/components/PendingTransactions.tsx:74
#, c-format
msgid "PENDING OPERATIONS"
-msgstr ""
+msgstr "BEKLEYEN İŞLEMLER"
#: src/components/Loading.tsx:36
#, c-format
@@ -111,42 +111,42 @@ msgstr "İçinde sona eriyor"
#: src/wallet/ProviderDetailPage.tsx:60
#, c-format
msgid "There was an error loading the provider detail for &quot; %1$s&quot;"
-msgstr ""
+msgstr "&quot;%1$s&quot; için sağlayıcı ayrıntısı yüklenirken bir hata oluştu"
#: src/wallet/ProviderDetailPage.tsx:108
#, c-format
msgid "There is not known provider with url &quot;%1$s&quot;."
-msgstr ""
+msgstr "&quot;%1$s&quot; URL'sine sahip bilinen bir sağlayıcı yok."
#: src/wallet/ProviderDetailPage.tsx:115
-#, fuzzy, c-format
+#, c-format
msgid "See providers"
-msgstr "Sağlayıcı ekle"
+msgstr "Sağlayıcıları gör"
#: src/wallet/ProviderDetailPage.tsx:143
#, c-format
msgid "Last backup"
-msgstr ""
+msgstr "Son yedek"
#: src/wallet/ProviderDetailPage.tsx:148
#, c-format
msgid "Back up"
-msgstr ""
+msgstr "Yedekle"
#: src/wallet/ProviderDetailPage.tsx:154
#, c-format
msgid "Provider fee"
-msgstr ""
+msgstr "Sağlayıcı ücreti"
#: src/wallet/ProviderDetailPage.tsx:157
#, c-format
msgid "per year"
-msgstr ""
+msgstr "yıllık"
#: src/wallet/ProviderDetailPage.tsx:163
#, c-format
msgid "Extend"
-msgstr ""
+msgstr "Uzat"
#: src/wallet/ProviderDetailPage.tsx:169
#, c-format
@@ -154,46 +154,48 @@ msgid ""
"terms has changed, extending the service will imply accepting the new terms "
"of service"
msgstr ""
+"şartlar değişti; hizmetin uzatılması yeni hizmet şartlarının kabul edilmesi "
+"anlamına gelecektir"
#: src/wallet/ProviderDetailPage.tsx:179
#, c-format
msgid "old"
-msgstr ""
+msgstr "eski"
#: src/wallet/ProviderDetailPage.tsx:183
#, c-format
msgid "new"
-msgstr ""
+msgstr "yeni"
#: src/wallet/ProviderDetailPage.tsx:190
#, c-format
msgid "fee"
-msgstr ""
+msgstr "ücret"
#: src/wallet/ProviderDetailPage.tsx:198
#, c-format
msgid "storage"
-msgstr ""
+msgstr "depolama"
#: src/wallet/ProviderDetailPage.tsx:215
#, c-format
msgid "Remove provider"
-msgstr ""
+msgstr "Sağlayıcıyı kaldır"
#: src/wallet/ProviderDetailPage.tsx:228
#, c-format
msgid "This provider has reported an error"
-msgstr ""
+msgstr "Sağlayıcı bir hata bildirdi"
#: src/wallet/ProviderDetailPage.tsx:242
#, c-format
msgid "There is conflict with another backup from %1$s"
-msgstr ""
+msgstr "%1$s başka bir yedekle çakışıyor"
#: src/wallet/ProviderDetailPage.tsx:253
#, c-format
msgid "Backup is not readable"
-msgstr ""
+msgstr "Yedek okunamıyor"
#: src/wallet/ProviderDetailPage.tsx:261
#, c-format
@@ -308,47 +310,47 @@ msgstr "Reddet"
#: src/popup/Application.tsx:177
#, c-format
msgid "this popup is being closed and you are being redirected to %1$s"
-msgstr ""
+msgstr "bu açılır pencere kapatılıyor ve %1$s adresine yönlendiriliyorsunuz"
#: src/components/ShowFullContractTermPopup.tsx:158
-#, fuzzy, c-format
+#, c-format
msgid "Could not load purchase proposal details"
-msgstr "Yedekleme sağlayıcıları yüklenemedi"
+msgstr "Satın alma teklifi ayrıntıları yüklenemedi"
#: src/components/ShowFullContractTermPopup.tsx:183
-#, fuzzy, c-format
+#, c-format
msgid "Order Id"
-msgstr "Sipariş reddedildi"
+msgstr "Sipariş kimliği"
#: src/components/ShowFullContractTermPopup.tsx:189
#, c-format
msgid "Summary"
-msgstr ""
+msgstr "Özet"
#: src/components/ShowFullContractTermPopup.tsx:195
#, c-format
msgid "Amount"
-msgstr ""
+msgstr "Miktar"
#: src/components/ShowFullContractTermPopup.tsx:203
#, c-format
msgid "Merchant name"
-msgstr ""
+msgstr "Satıcı adı"
#: src/components/ShowFullContractTermPopup.tsx:209
-#, c-format
+#, c-format, fuzzy
msgid "Merchant jurisdiction"
-msgstr ""
+msgstr "Satıcının yetki alanı"
#: src/components/ShowFullContractTermPopup.tsx:215
#, c-format
msgid "Merchant address"
-msgstr ""
+msgstr "Satıcı adresi"
#: src/components/ShowFullContractTermPopup.tsx:221
#, c-format
msgid "Merchant logo"
-msgstr ""
+msgstr "Satıcı logosu"
#: src/components/ShowFullContractTermPopup.tsx:234
#, c-format
@@ -363,112 +365,112 @@ msgstr "Satıcı e-postası"
#: src/components/ShowFullContractTermPopup.tsx:246
#, c-format
msgid "Merchant public key"
-msgstr ""
+msgstr "Satıcı genel anahtarı"
#: src/components/ShowFullContractTermPopup.tsx:256
#, c-format
msgid "Delivery date"
-msgstr ""
+msgstr "Teslim tarihi"
#: src/components/ShowFullContractTermPopup.tsx:271
-#, fuzzy, c-format
+#, c-format
msgid "Delivery location"
-msgstr "Taler Eylemi"
+msgstr "Teslimat yeri"
#: src/components/ShowFullContractTermPopup.tsx:277
#, c-format
msgid "Products"
-msgstr ""
+msgstr "Ürünler"
#: src/components/ShowFullContractTermPopup.tsx:289
#, c-format
msgid "Created at"
-msgstr ""
+msgstr "Oluşturulma"
#: src/components/ShowFullContractTermPopup.tsx:304
#, c-format
msgid "Refund deadline"
-msgstr ""
+msgstr "Geri ödeme son tarihi"
#: src/components/ShowFullContractTermPopup.tsx:319
-#, fuzzy, c-format
+#, c-format
msgid "Auto refund"
-msgstr "Ödeme iadesi"
+msgstr "Otomatik geri ödeme"
#: src/components/ShowFullContractTermPopup.tsx:339
#, c-format
msgid "Pay deadline"
-msgstr ""
+msgstr "Son ödeme tarihi"
#: src/components/ShowFullContractTermPopup.tsx:354
#, c-format
msgid "Fulfillment URL"
-msgstr ""
+msgstr "Gönderim URL'si"
#: src/components/ShowFullContractTermPopup.tsx:360
#, c-format
msgid "Fulfillment message"
-msgstr ""
+msgstr "Gönderim mesajı"
#: src/components/ShowFullContractTermPopup.tsx:370
#, c-format
msgid "Max deposit fee"
-msgstr ""
+msgstr "Azami para yatırma ücreti"
#: src/components/ShowFullContractTermPopup.tsx:378
#, c-format
msgid "Max fee"
-msgstr ""
+msgstr "Azami ücret"
#: src/components/ShowFullContractTermPopup.tsx:386
#, c-format
msgid "Minimum age"
-msgstr ""
+msgstr "Asgari yaş"
#: src/components/ShowFullContractTermPopup.tsx:398
#, c-format
msgid "Wire fee amortization"
-msgstr ""
+msgstr "Banka havalesi amortismanı"
#: src/components/ShowFullContractTermPopup.tsx:404
#, c-format
msgid "Auditors"
-msgstr ""
+msgstr "Denetçiler"
#: src/components/ShowFullContractTermPopup.tsx:419
-#, fuzzy, c-format
+#, c-format
msgid "Exchanges"
-msgstr "Exchange"
+msgstr "Borsalar"
#: src/components/Part.tsx:148
#, c-format
msgid "Bank account"
-msgstr ""
+msgstr "Banka hesabı"
#: src/components/Part.tsx:160
#, c-format
msgid "Bitcoin address"
-msgstr ""
+msgstr "Bitcoin adresi"
#: src/components/Part.tsx:163
#, c-format
msgid "IBAN"
-msgstr ""
+msgstr "IBAN"
#: src/cta/Deposit/views.tsx:38
-#, fuzzy, c-format
+#, c-format
msgid "Could not load deposit status"
-msgstr "Bakiye sayfası yüklenemedi"
+msgstr "Para yatırma durumu yüklenemedi"
#: src/cta/Deposit/views.tsx:52
#, c-format
msgid "Digital cash deposit"
-msgstr ""
+msgstr "Dijital para yatırma"
#: src/cta/Deposit/views.tsx:58
#, c-format
msgid "Cost"
-msgstr ""
+msgstr "Maliyet"
#: src/cta/Deposit/views.tsx:66
#, c-format
@@ -478,17 +480,17 @@ msgstr "Ücretler"
#: src/cta/Deposit/views.tsx:73
#, c-format
msgid "To be received"
-msgstr ""
+msgstr "Alınacak"
#: src/cta/Deposit/views.tsx:84
#, c-format
msgid "Send &nbsp; %1$s"
-msgstr ""
+msgstr "&nbsp;%1$s gönder"
#: src/components/BankDetailsByPaytoType.tsx:63
#, c-format
msgid "Bitcoin transfer details"
-msgstr ""
+msgstr "Bitcoin transfer ayrıntıları"
#: src/components/BankDetailsByPaytoType.tsx:66
#, c-format
@@ -515,7 +517,7 @@ msgstr ""
#: src/components/BankDetailsByPaytoType.tsx:110
#, c-format
msgid "Account"
-msgstr ""
+msgstr "Hesap"
#: src/components/BankDetailsByPaytoType.tsx:116
#, c-format
@@ -530,7 +532,7 @@ msgstr ""
#: src/components/BankDetailsByPaytoType.tsx:148
#, c-format
msgid "Subject"
-msgstr ""
+msgstr "Konu"
#: src/components/BankDetailsByPaytoType.tsx:154
#, c-format
@@ -555,7 +557,7 @@ msgstr ""
#: src/wallet/Transaction.tsx:209
#, c-format
msgid "Send"
-msgstr ""
+msgstr "Göder"
#: src/wallet/Transaction.tsx:216
#, c-format
@@ -565,12 +567,12 @@ msgstr "Yeniden deneyin"
#: src/wallet/Transaction.tsx:224
#, c-format
msgid "Forget"
-msgstr ""
+msgstr "Unut"
#: src/wallet/Transaction.tsx:241
#, c-format
msgid "Caution!"
-msgstr ""
+msgstr "Dikkat!"
#: src/wallet/Transaction.tsx:244
#, c-format
@@ -613,7 +615,7 @@ msgstr ""
#: src/wallet/Transaction.tsx:325
#, c-format
msgid "Details"
-msgstr ""
+msgstr "Detaylar"
#: src/wallet/Transaction.tsx:360
#, fuzzy, c-format
@@ -639,7 +641,7 @@ msgstr ""
#: src/wallet/Transaction.tsx:420
#, c-format
msgid "Offer"
-msgstr ""
+msgstr "Teklif"
#: src/wallet/Transaction.tsx:431
#, fuzzy, c-format
@@ -649,17 +651,17 @@ msgstr "İkramiye kabul edildi"
#: src/wallet/Transaction.tsx:438
#, c-format
msgid "Merchant"
-msgstr ""
+msgstr "Satıcı"
#: src/wallet/Transaction.tsx:443
#, c-format
msgid "Invoice ID"
-msgstr ""
+msgstr "Fatura Kimliği"
#: src/wallet/Transaction.tsx:470
#, c-format
msgid "Deposit"
-msgstr ""
+msgstr "Yatır"
#: src/wallet/Transaction.tsx:496
#, fuzzy, c-format
@@ -669,7 +671,7 @@ msgstr "Ücreti yenile"
#: src/wallet/Transaction.tsx:517
#, c-format
msgid "Tip"
-msgstr ""
+msgstr "Bahşiş"
#: src/wallet/Transaction.tsx:542
#, c-format
@@ -689,27 +691,27 @@ msgstr ""
#: src/wallet/Transaction.tsx:593
#, c-format
msgid "copy"
-msgstr ""
+msgstr "kopyala"
#: src/wallet/Transaction.tsx:596
#, c-format
msgid "hide qr"
-msgstr ""
+msgstr "qr'yi gizle"
#: src/wallet/Transaction.tsx:608
#, c-format
msgid "show qr"
-msgstr ""
+msgstr "qr'yi göster"
#: src/wallet/Transaction.tsx:620
#, c-format
msgid "Credit"
-msgstr ""
+msgstr "Kredi"
#: src/wallet/Transaction.tsx:624
#, c-format
msgid "Invoice"
-msgstr ""
+msgstr "Fatura"
#: src/wallet/Transaction.tsx:635
#, c-format
@@ -719,7 +721,7 @@ msgstr "Exchange"
#: src/wallet/Transaction.tsx:641
#, c-format
msgid "URI"
-msgstr ""
+msgstr "URI"
#: src/wallet/Transaction.tsx:667
#, c-format
@@ -729,52 +731,52 @@ msgstr ""
#: src/wallet/Transaction.tsx:710
#, c-format
msgid "Transfer"
-msgstr ""
+msgstr "Transfer"
#: src/wallet/Transaction.tsx:844
#, c-format
msgid "Country"
-msgstr ""
+msgstr "Ülke"
#: src/wallet/Transaction.tsx:852
#, c-format
msgid "Address lines"
-msgstr ""
+msgstr "Adres satırı"
#: src/wallet/Transaction.tsx:860
#, c-format
msgid "Building number"
-msgstr ""
+msgstr "Bina numarası"
#: src/wallet/Transaction.tsx:868
#, c-format
msgid "Building name"
-msgstr ""
+msgstr "Bina adı"
#: src/wallet/Transaction.tsx:876
#, c-format
msgid "Street"
-msgstr ""
+msgstr "Sokak"
#: src/wallet/Transaction.tsx:884
#, c-format
msgid "Post code"
-msgstr ""
+msgstr "Posta kodu"
#: src/wallet/Transaction.tsx:892
#, c-format
msgid "Town location"
-msgstr ""
+msgstr "Kasaba konumu"
#: src/wallet/Transaction.tsx:900
#, c-format
msgid "Town"
-msgstr ""
+msgstr "Kasaba"
#: src/wallet/Transaction.tsx:908
#, c-format
msgid "District"
-msgstr ""
+msgstr "Semt"
#: src/wallet/Transaction.tsx:916
#, c-format
@@ -784,17 +786,17 @@ msgstr ""
#: src/wallet/Transaction.tsx:935
#, c-format
msgid "Date"
-msgstr ""
+msgstr "Tarih"
#: src/wallet/Transaction.tsx:990
#, c-format
msgid "Transaction fees"
-msgstr ""
+msgstr "İşlem ücretleri"
#: src/wallet/Transaction.tsx:1004
#, c-format
msgid "Total"
-msgstr ""
+msgstr "Toplam"
#: src/wallet/Transaction.tsx:1074
#, c-format
@@ -804,17 +806,17 @@ msgstr "Para çek"
#: src/wallet/Transaction.tsx:1146
#, c-format
msgid "Price"
-msgstr ""
+msgstr "Fiyat"
#: src/wallet/Transaction.tsx:1156
#, c-format
msgid "Refunded"
-msgstr ""
+msgstr "İade edildi"
#: src/wallet/Transaction.tsx:1220
#, c-format
msgid "Delivery"
-msgstr ""
+msgstr "Teslimat"
#: src/wallet/Transaction.tsx:1335
#, fuzzy, c-format
@@ -824,77 +826,77 @@ msgstr "Çekildi"
#: src/cta/Payment/views.tsx:57
#, c-format
msgid "Could not load pay status"
-msgstr ""
+msgstr "Ödeme durumu yüklenemedi"
#: src/cta/Payment/views.tsx:87
#, c-format
msgid "Digital cash payment"
-msgstr ""
+msgstr "Dijital ödeme"
#: src/cta/Payment/views.tsx:119
#, c-format
msgid "Purchase"
-msgstr ""
+msgstr "Satın al"
#: src/cta/Payment/views.tsx:149
#, c-format
msgid "Receipt"
-msgstr ""
+msgstr "Fiş"
#: src/cta/Payment/views.tsx:156
#, c-format
msgid "Valid until"
-msgstr ""
+msgstr "Geçerlilik"
#: src/cta/Payment/views.tsx:191
#, c-format
msgid "List of products"
-msgstr ""
+msgstr "Ürün listesi"
#: src/cta/Payment/views.tsx:242
#, c-format
msgid "free"
-msgstr ""
+msgstr "ücretsiz"
#: src/cta/Payment/views.tsx:263
#, c-format
msgid "Already paid, you are going to be redirected to %1$s"
-msgstr ""
+msgstr "Zaten ödeme yaptınız, %1$s adresine yönlendirileceksiniz"
#: src/cta/Payment/views.tsx:274
#, c-format
msgid "Already paid"
-msgstr ""
+msgstr "Zaten ödendi"
#: src/cta/Payment/views.tsx:280
#, c-format
msgid "Already claimed"
-msgstr ""
+msgstr "Zaten talep edilmiş"
#: src/cta/Payment/views.tsx:296
#, c-format
msgid "Pay with a mobile phone"
-msgstr ""
+msgstr "Cep telefonuyla ödeme yapın"
#: src/cta/Payment/views.tsx:298
#, c-format
msgid "Hide QR"
-msgstr ""
+msgstr "QR'yi gizle"
#: src/cta/Payment/views.tsx:305
#, c-format
msgid "Scan the QR code or &nbsp; %1$s"
-msgstr ""
+msgstr "QR kodunu tarayın veya&nbsp;%1$s"
#: src/cta/Payment/views.tsx:346
#, c-format
msgid "Pay &nbsp; %1$s"
-msgstr ""
+msgstr "%1$snbsp;öde"
#: src/cta/Payment/views.tsx:360
#, c-format
msgid "You have no balance for this currency. Withdraw digital cash first."
-msgstr ""
+msgstr "Bu para birimi için bakiyeniz yok. Önce dijital parayı çekin."
#: src/cta/Payment/views.tsx:364
#, c-format
@@ -902,6 +904,8 @@ msgid ""
"Could not find enough coins to pay. Even if you have enough %1$s some "
"restriction may apply."
msgstr ""
+"Ödemeye yetecek kadar para bulunamadı. Yeterli %1$s olsa bile bazı "
+"kısıtlamalar geçerli olabilir."
#: src/cta/Payment/views.tsx:366
#, fuzzy, c-format
@@ -911,7 +915,7 @@ msgstr "Gösterecek bakiyeniz yok."
#: src/cta/Payment/views.tsx:395
#, c-format
msgid "Merchant message"
-msgstr ""
+msgstr "Satıcı mesajı"
#: src/cta/Refund/views.tsx:34
#, fuzzy, c-format
@@ -1286,7 +1290,7 @@ msgstr ""
#: src/wallet/CreateManualWithdraw.tsx:277
#, c-format
msgid "Start withdrawal"
-msgstr ""
+msgstr "Para çekme işlemini başlat"
#: src/wallet/DepositPage/views.tsx:38
#, fuzzy, c-format
diff --git a/packages/taler-wallet-webextension/src/i18n/uk.po b/packages/taler-wallet-webextension/src/i18n/uk.po
index c4f5d6537..caee8330e 100644
--- a/packages/taler-wallet-webextension/src/i18n/uk.po
+++ b/packages/taler-wallet-webextension/src/i18n/uk.po
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: languages@taler.net\n"
"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2024-03-05 13:03+0000\n"
-"Last-Translator: Tim Vutor <flukes.ostrich0p@icloud.com>\n"
+"PO-Revision-Date: 2024-08-07 10:40+0000\n"
+"Last-Translator: Vlada Svirsh <vlada.svirsh@students.bfh.ch>\n"
"Language-Team: Ukrainian <https://weblate.taler.net/projects/gnu-taler/"
"webextensions/uk/>\n"
"Language: uk\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 5.2.1\n"
+"X-Generator: Weblate 5.5.5\n"
#: src/NavigationBar.tsx:139
#, c-format
@@ -28,12 +28,12 @@ msgstr "Баланс"
#: src/NavigationBar.tsx:142
#, c-format
msgid "Backup"
-msgstr "Бекап"
+msgstr "Резервна копія"
#: src/NavigationBar.tsx:147
#, c-format
msgid "QR Reader and Taler URI"
-msgstr "QR-читалка та Taler URI"
+msgstr "Зчитувач QR та Taler URI"
#: src/NavigationBar.tsx:154
#, c-format
@@ -46,14 +46,14 @@ msgid "Dev"
msgstr "Розробка"
#: src/mui/Typography.tsx:122
-#, c-format, fuzzy
+#, c-format
msgid "%1$s"
msgstr "%1$s"
#: src/components/PendingTransactions.tsx:74
#, c-format
msgid "PENDING OPERATIONS"
-msgstr "НЕЗАВЕРШЕНІ ОПЕРАЦІЇ"
+msgstr "ОЧІКУВАНІ ОПЕРАЦІЇ"
#: src/components/Loading.tsx:36
#, c-format
@@ -177,7 +177,7 @@ msgstr "Видалити зберігача"
#: src/wallet/ProviderDetailPage.tsx:228
#, c-format
msgid "This provider has reported an error"
-msgstr "Цей постачальник повідомив про помилку"
+msgstr "Цей зберігач повідомив про помилку"
#: src/wallet/ProviderDetailPage.tsx:242
#, c-format
@@ -207,32 +207,32 @@ msgstr "Резервна копія дійсна до"
#: src/wallet/AddNewActionView.tsx:57
#, c-format
msgid "Cancel"
-msgstr "Відмінити"
+msgstr "Скасувати"
#: src/wallet/AddNewActionView.tsx:68
#, c-format
msgid "Open reserve page"
-msgstr "Показати резерв"
+msgstr "Відкрити резервну сторінку"
#: src/wallet/AddNewActionView.tsx:70
#, c-format
msgid "Open pay page"
-msgstr "Показати сторінку оплати"
+msgstr "Відкрити сторінку оплати"
#: src/wallet/AddNewActionView.tsx:72
#, c-format
msgid "Open refund page"
-msgstr "Показати відшкодування"
+msgstr "Відкрити сторінку повернень"
#: src/wallet/AddNewActionView.tsx:74
#, c-format
msgid "Open tip page"
-msgstr "Показати чайові"
+msgstr "Відкрити сторінку чайових"
#: src/wallet/AddNewActionView.tsx:76
#, c-format
msgid "Open withdraw page"
-msgstr "Показати списання"
+msgstr "Відкрити сторінку зняття"
#: src/popup/NoBalanceHelp.tsx:43
#, c-format
@@ -242,7 +242,7 @@ msgstr "Отримати е-готівку"
#: src/popup/BalancePage.tsx:138
#, c-format
msgid "Could not load balance page"
-msgstr "Не вдалося показати залишок"
+msgstr "Не вдалося завантажити сторінку балансу"
#: src/popup/BalancePage.tsx:175
#, c-format
@@ -262,227 +262,227 @@ msgstr "Taler Дія"
#: src/popup/TalerActionFound.tsx:49
#, c-format
msgid "This page has pay action."
-msgstr ""
+msgstr "Ця сторінка має дію оплати."
#: src/popup/TalerActionFound.tsx:63
#, c-format
msgid "This page has a withdrawal action."
-msgstr ""
+msgstr "Ця сторінка має дію зняття коштів."
#: src/popup/TalerActionFound.tsx:79
#, c-format
msgid "This page has a tip action."
-msgstr ""
+msgstr "Ця сторінка має дію чайових."
#: src/popup/TalerActionFound.tsx:93
#, c-format
msgid "This page has a notify reserve action."
-msgstr ""
+msgstr "Ця сторінка має дію сповіщення про резерв."
#: src/popup/TalerActionFound.tsx:102
#, c-format
msgid "Notify"
-msgstr ""
+msgstr "Сповістити"
#: src/popup/TalerActionFound.tsx:109
#, c-format
msgid "This page has a refund action."
-msgstr ""
+msgstr "Ця сторінка має дію повернення коштів."
#: src/popup/TalerActionFound.tsx:123
#, c-format
msgid "This page has a malformed taler uri."
-msgstr ""
+msgstr "Ця сторінка має некоректний URI Taler."
#: src/popup/TalerActionFound.tsx:134
#, c-format
msgid "Dismiss"
-msgstr ""
+msgstr "Закрити"
#: src/popup/Application.tsx:177
#, c-format
msgid "this popup is being closed and you are being redirected to %1$s"
-msgstr ""
+msgstr "це спливаюче вікно закривається, і ви перенаправляєтеся на %1$s"
#: src/components/ShowFullContractTermPopup.tsx:158
#, c-format
msgid "Could not load purchase proposal details"
-msgstr ""
+msgstr "Не вдалося завантажити деталі пропозиції покупки"
#: src/components/ShowFullContractTermPopup.tsx:183
#, c-format
msgid "Order Id"
-msgstr ""
+msgstr "Номер замовлення"
#: src/components/ShowFullContractTermPopup.tsx:189
#, c-format
msgid "Summary"
-msgstr ""
+msgstr "Підсумок"
#: src/components/ShowFullContractTermPopup.tsx:195
#, c-format
msgid "Amount"
-msgstr ""
+msgstr "Сума"
#: src/components/ShowFullContractTermPopup.tsx:203
#, c-format
msgid "Merchant name"
-msgstr ""
+msgstr "Ім'я продавця"
#: src/components/ShowFullContractTermPopup.tsx:209
#, c-format
msgid "Merchant jurisdiction"
-msgstr ""
+msgstr "Юрисдикція продавця"
#: src/components/ShowFullContractTermPopup.tsx:215
#, c-format
msgid "Merchant address"
-msgstr ""
+msgstr "Адреса продавця"
#: src/components/ShowFullContractTermPopup.tsx:221
#, c-format
msgid "Merchant logo"
-msgstr ""
+msgstr "Логотип продавця"
#: src/components/ShowFullContractTermPopup.tsx:234
#, c-format
msgid "Merchant website"
-msgstr ""
+msgstr "Вебсайт продавця"
#: src/components/ShowFullContractTermPopup.tsx:240
#, c-format
msgid "Merchant email"
-msgstr ""
+msgstr "Електронна пошта продавця"
#: src/components/ShowFullContractTermPopup.tsx:246
#, c-format
msgid "Merchant public key"
-msgstr ""
+msgstr "Публічний ключ продавця"
#: src/components/ShowFullContractTermPopup.tsx:256
#, c-format
msgid "Delivery date"
-msgstr ""
+msgstr "Дата доставки"
#: src/components/ShowFullContractTermPopup.tsx:271
#, c-format
msgid "Delivery location"
-msgstr ""
+msgstr "Місце доставки"
#: src/components/ShowFullContractTermPopup.tsx:277
#, c-format
msgid "Products"
-msgstr ""
+msgstr "Товари"
#: src/components/ShowFullContractTermPopup.tsx:289
#, c-format
msgid "Created at"
-msgstr ""
+msgstr "Створено о"
#: src/components/ShowFullContractTermPopup.tsx:304
#, c-format
msgid "Refund deadline"
-msgstr ""
+msgstr "Термін повернення"
#: src/components/ShowFullContractTermPopup.tsx:319
#, c-format
msgid "Auto refund"
-msgstr ""
+msgstr "Автоматичне повернення"
#: src/components/ShowFullContractTermPopup.tsx:339
#, c-format
msgid "Pay deadline"
-msgstr ""
+msgstr "Термін оплати"
#: src/components/ShowFullContractTermPopup.tsx:354
#, c-format
msgid "Fulfillment URL"
-msgstr ""
+msgstr "URL виконання"
#: src/components/ShowFullContractTermPopup.tsx:360
#, c-format
msgid "Fulfillment message"
-msgstr ""
+msgstr "Повідомлення про виконання"
#: src/components/ShowFullContractTermPopup.tsx:370
#, c-format
msgid "Max deposit fee"
-msgstr ""
+msgstr "Максимальна комісія за депозит"
#: src/components/ShowFullContractTermPopup.tsx:378
#, c-format
msgid "Max fee"
-msgstr ""
+msgstr "Максимальна комісія"
#: src/components/ShowFullContractTermPopup.tsx:386
#, c-format
msgid "Minimum age"
-msgstr ""
+msgstr "Мінімальний вік"
#: src/components/ShowFullContractTermPopup.tsx:398
#, c-format
msgid "Wire fee amortization"
-msgstr ""
+msgstr "Амортизація комісії за переказ"
#: src/components/ShowFullContractTermPopup.tsx:404
#, c-format
msgid "Auditors"
-msgstr ""
+msgstr "Аудитори"
#: src/components/ShowFullContractTermPopup.tsx:419
#, c-format
msgid "Exchanges"
-msgstr ""
+msgstr "Обмінники"
#: src/components/Part.tsx:148
#, c-format
msgid "Bank account"
-msgstr ""
+msgstr "Банківський рахунок"
#: src/components/Part.tsx:160
#, c-format
msgid "Bitcoin address"
-msgstr ""
+msgstr "Адреса Bitcoin"
#: src/components/Part.tsx:163
#, c-format
msgid "IBAN"
-msgstr ""
+msgstr "IBAN"
#: src/cta/Deposit/views.tsx:38
#, c-format
msgid "Could not load deposit status"
-msgstr ""
+msgstr "Не вдалося завантажити статус депозиту"
#: src/cta/Deposit/views.tsx:52
#, c-format
msgid "Digital cash deposit"
-msgstr ""
+msgstr "Депозит електронних грошей"
#: src/cta/Deposit/views.tsx:58
#, c-format
msgid "Cost"
-msgstr ""
+msgstr "Вартість"
#: src/cta/Deposit/views.tsx:66
#, c-format
msgid "Fee"
-msgstr ""
+msgstr "Комісія"
#: src/cta/Deposit/views.tsx:73
#, c-format
msgid "To be received"
-msgstr ""
+msgstr "До отримання"
#: src/cta/Deposit/views.tsx:84
#, c-format
msgid "Send &nbsp; %1$s"
-msgstr ""
+msgstr "Надіслати &nbsp; %1$s"
#: src/components/BankDetailsByPaytoType.tsx:63
#, c-format
msgid "Bitcoin transfer details"
-msgstr ""
+msgstr "Деталі переказу Bitcoin"
#: src/components/BankDetailsByPaytoType.tsx:66
#, c-format
@@ -491,6 +491,8 @@ msgid ""
"account and the other two are segwit fake address for metadata with an minimum "
"amount."
msgstr ""
+"Для обміну потрібна транзакція з 3 виходами: один вихід - це рахунок обміну, "
+"а два інші - segwit вигадані адреси для метаданих з мінімальною сумою."
#: src/components/BankDetailsByPaytoType.tsx:74
#, c-format
@@ -498,71 +500,75 @@ msgid ""
"In bitcoincore wallet use &apos;Add Recipient&apos; button to add two additional "
"recipient and copy addresses and amounts"
msgstr ""
+"У гаманці bitcoincore використовуйте кнопку &apos;Додати отримувача&apos;, "
+"щоб додати двох додаткових отримувачів і скопіювати адреси та суми"
#: src/components/BankDetailsByPaytoType.tsx:98
#, c-format
msgid "Make sure the amount show %1$s BTC, else you have to change the base unit to BTC"
msgstr ""
+"Переконайтеся, що сума показує %1$s BTC, інакше вам потрібно змінити базову "
+"одиницю на BTC"
#: src/components/BankDetailsByPaytoType.tsx:110
#, c-format
msgid "Account"
-msgstr ""
+msgstr "Рахунок"
#: src/components/BankDetailsByPaytoType.tsx:116
#, c-format
msgid "Bank host"
-msgstr ""
+msgstr "Банк-хост"
#: src/components/BankDetailsByPaytoType.tsx:139
#, c-format
msgid "Bank transfer details"
-msgstr ""
+msgstr "Деталі банківського переказу"
#: src/components/BankDetailsByPaytoType.tsx:148
#, c-format
msgid "Subject"
-msgstr ""
+msgstr "Призначення"
#: src/components/BankDetailsByPaytoType.tsx:154
#, c-format
msgid "Receiver name"
-msgstr ""
+msgstr "Ім'я отримувача"
#: src/wallet/Transaction.tsx:98
#, c-format
msgid "Could not load the transaction information"
-msgstr ""
+msgstr "Не вдалося завантажити інформацію про транзакцію"
#: src/wallet/Transaction.tsx:191
#, c-format
msgid "There was an error trying to complete the transaction"
-msgstr ""
+msgstr "Сталася помилка під час спроби завершити транзакцію"
#: src/wallet/Transaction.tsx:200
#, c-format
msgid "This transaction is not completed"
-msgstr ""
+msgstr "Ця транзакція не завершена"
#: src/wallet/Transaction.tsx:209
#, c-format
msgid "Send"
-msgstr ""
+msgstr "Надіслати"
#: src/wallet/Transaction.tsx:216
#, c-format
msgid "Retry"
-msgstr ""
+msgstr "Спробувати ще раз"
#: src/wallet/Transaction.tsx:224
#, c-format
msgid "Forget"
-msgstr ""
+msgstr "Забути"
#: src/wallet/Transaction.tsx:241
#, c-format
msgid "Caution!"
-msgstr ""
+msgstr "Увага!"
#: src/wallet/Transaction.tsx:244
#, c-format
@@ -570,16 +576,18 @@ msgid ""
"If you have already wired money to the exchange you will loose the chance to get "
"the coins form it."
msgstr ""
+"Якщо Ви вже переказали гроші на обмінник, Ви втратите можливість отримати з "
+"нього монети."
#: src/wallet/Transaction.tsx:259
#, c-format
msgid "Confirm"
-msgstr ""
+msgstr "Підтвердити"
#: src/wallet/Transaction.tsx:267
#, c-format
msgid "Withdrawal"
-msgstr ""
+msgstr "Зняття"
#: src/wallet/Transaction.tsx:286
#, c-format
@@ -587,6 +595,8 @@ msgid ""
"Make sure to use the correct subject, otherwise the money will not arrive in "
"this wallet."
msgstr ""
+"Переконайтеся, що використовуєте правильне призначення, інакше гроші не "
+"надійдуть на цей гаманець."
#: src/wallet/Transaction.tsx:298
#, c-format
@@ -594,296 +604,301 @@ msgid ""
"The bank did not yet confirmed the wire transfer. Go to the %1$s %2$s and check "
"there is no pending step."
msgstr ""
+"Банк ще не підтвердив банківський переказ. Перейдіть до %1$s %2$s і "
+"переконайтеся, що немає жодного незавершеного кроку."
#: src/wallet/Transaction.tsx:316
#, c-format
msgid "Bank has confirmed the wire transfer. Waiting for the exchange to send the coins"
msgstr ""
+"Банк підтвердив банківський переказ. Очікуємо, що обмінник надішле монети"
#: src/wallet/Transaction.tsx:325
#, c-format
msgid "Details"
-msgstr ""
+msgstr "Деталі"
#: src/wallet/Transaction.tsx:360
#, c-format
msgid "Payment"
-msgstr ""
+msgstr "Оплата"
#: src/wallet/Transaction.tsx:378
#, c-format
msgid "Refunds"
-msgstr ""
+msgstr "Повернення"
#: src/wallet/Transaction.tsx:385
#, c-format
msgid "%1$s %2$s on %3$s"
-msgstr ""
+msgstr "%1$s %2$s на %3$s"
#: src/wallet/Transaction.tsx:415
#, c-format
msgid "Merchant created a refund for this order but was not automatically picked up."
msgstr ""
+"Продавець створив повернення для цього замовлення, але воно не було "
+"автоматично оброблене."
#: src/wallet/Transaction.tsx:420
#, c-format
msgid "Offer"
-msgstr ""
+msgstr "Пропозиція"
#: src/wallet/Transaction.tsx:431
#, c-format
msgid "Accept"
-msgstr ""
+msgstr "Прийняти"
#: src/wallet/Transaction.tsx:438
#, c-format
msgid "Merchant"
-msgstr ""
+msgstr "Продавець"
#: src/wallet/Transaction.tsx:443
#, c-format
msgid "Invoice ID"
-msgstr ""
+msgstr "№ рахунку-фактури"
#: src/wallet/Transaction.tsx:470
#, c-format
msgid "Deposit"
-msgstr ""
+msgstr "Депозит"
#: src/wallet/Transaction.tsx:496
#, c-format
msgid "Refresh"
-msgstr ""
+msgstr "Оновити"
#: src/wallet/Transaction.tsx:517
#, c-format
msgid "Tip"
-msgstr ""
+msgstr "Чайові"
#: src/wallet/Transaction.tsx:542
#, c-format
msgid "Refund"
-msgstr ""
+msgstr "Повернення"
#: src/wallet/Transaction.tsx:555
#, c-format
msgid "Original order ID"
-msgstr ""
+msgstr "Початковий № замовлення"
#: src/wallet/Transaction.tsx:568
#, c-format
msgid "Purchase summary"
-msgstr ""
+msgstr "Інформація про покупку"
#: src/wallet/Transaction.tsx:593
#, c-format
msgid "copy"
-msgstr ""
+msgstr "копіювати"
#: src/wallet/Transaction.tsx:596
#, c-format
msgid "hide qr"
-msgstr ""
+msgstr "сховати qr"
#: src/wallet/Transaction.tsx:608
#, c-format
msgid "show qr"
-msgstr ""
+msgstr "показати qr"
#: src/wallet/Transaction.tsx:620
#, c-format
msgid "Credit"
-msgstr ""
+msgstr "Кредит"
#: src/wallet/Transaction.tsx:624
#, c-format
msgid "Invoice"
-msgstr ""
+msgstr "Рахунок-фактура"
#: src/wallet/Transaction.tsx:635
#, c-format
msgid "Exchange"
-msgstr ""
+msgstr "Exchange"
#: src/wallet/Transaction.tsx:641
#, c-format
msgid "URI"
-msgstr ""
+msgstr "URI"
#: src/wallet/Transaction.tsx:667
#, c-format
msgid "Debit"
-msgstr ""
+msgstr "Дебет"
#: src/wallet/Transaction.tsx:710
#, c-format
msgid "Transfer"
-msgstr ""
+msgstr "Переказати"
#: src/wallet/Transaction.tsx:844
#, c-format
msgid "Country"
-msgstr ""
+msgstr "Країна"
#: src/wallet/Transaction.tsx:852
#, c-format
msgid "Address lines"
-msgstr ""
+msgstr "Рядки адреси"
#: src/wallet/Transaction.tsx:860
#, c-format
msgid "Building number"
-msgstr ""
+msgstr "Номер будинку"
#: src/wallet/Transaction.tsx:868
#, c-format
msgid "Building name"
-msgstr ""
+msgstr "Назва будинку"
#: src/wallet/Transaction.tsx:876
#, c-format
msgid "Street"
-msgstr ""
+msgstr "Вулиця"
#: src/wallet/Transaction.tsx:884
#, c-format
msgid "Post code"
-msgstr ""
+msgstr "Поштовий індекс"
#: src/wallet/Transaction.tsx:892
#, c-format
msgid "Town location"
-msgstr ""
+msgstr "Область міста"
#: src/wallet/Transaction.tsx:900
#, c-format
msgid "Town"
-msgstr ""
+msgstr "Місто"
#: src/wallet/Transaction.tsx:908
#, c-format
msgid "District"
-msgstr ""
+msgstr "Район"
#: src/wallet/Transaction.tsx:916
#, c-format
msgid "Country subdivision"
-msgstr ""
+msgstr "Регіон країни"
#: src/wallet/Transaction.tsx:935
#, c-format
msgid "Date"
-msgstr ""
+msgstr "Дата"
#: src/wallet/Transaction.tsx:990
#, c-format
msgid "Transaction fees"
-msgstr ""
+msgstr "Комісії за транзакцію"
#: src/wallet/Transaction.tsx:1004
#, c-format
msgid "Total"
-msgstr ""
+msgstr "Загальна сума"
#: src/wallet/Transaction.tsx:1074
#, c-format
msgid "Withdraw"
-msgstr ""
+msgstr "Зняття коштів"
#: src/wallet/Transaction.tsx:1146
#, c-format
msgid "Price"
-msgstr ""
+msgstr "Ціна"
#: src/wallet/Transaction.tsx:1156
#, c-format
msgid "Refunded"
-msgstr ""
+msgstr "Повернено"
#: src/wallet/Transaction.tsx:1220
#, c-format
msgid "Delivery"
-msgstr ""
+msgstr "Доставка"
#: src/wallet/Transaction.tsx:1335
#, c-format
msgid "Total transfer"
-msgstr ""
+msgstr "Загальний переказ"
#: src/cta/Payment/views.tsx:57
#, c-format
msgid "Could not load pay status"
-msgstr ""
+msgstr "Не вдалося завантажити статус оплати"
#: src/cta/Payment/views.tsx:87
#, c-format
msgid "Digital cash payment"
-msgstr ""
+msgstr "Оплата електронними грошима"
#: src/cta/Payment/views.tsx:119
#, c-format
msgid "Purchase"
-msgstr ""
+msgstr "Покупка"
#: src/cta/Payment/views.tsx:149
#, c-format
msgid "Receipt"
-msgstr ""
+msgstr "Чек"
#: src/cta/Payment/views.tsx:156
#, c-format
msgid "Valid until"
-msgstr ""
+msgstr "Дійсний до"
#: src/cta/Payment/views.tsx:191
#, c-format
msgid "List of products"
-msgstr ""
+msgstr "Список продуктів"
#: src/cta/Payment/views.tsx:242
#, c-format
msgid "free"
-msgstr ""
+msgstr "безкоштовно"
#: src/cta/Payment/views.tsx:263
#, c-format
msgid "Already paid, you are going to be redirected to %1$s"
-msgstr ""
+msgstr "Вже оплачено, Вас буде перенаправлено на %1$s"
#: src/cta/Payment/views.tsx:274
#, c-format
msgid "Already paid"
-msgstr ""
+msgstr "Вже оплачено"
#: src/cta/Payment/views.tsx:280
#, c-format
msgid "Already claimed"
-msgstr ""
+msgstr "Вже отримано"
#: src/cta/Payment/views.tsx:296
#, c-format
msgid "Pay with a mobile phone"
-msgstr ""
+msgstr "Оплатити мобільним телефоном"
#: src/cta/Payment/views.tsx:298
#, c-format
msgid "Hide QR"
-msgstr ""
+msgstr "Сховати QR"
#: src/cta/Payment/views.tsx:305
#, c-format
msgid "Scan the QR code or &nbsp; %1$s"
-msgstr ""
+msgstr "Скануйте QR-код або &nbsp; %1$s"
#: src/cta/Payment/views.tsx:346
#, c-format
msgid "Pay &nbsp; %1$s"
-msgstr ""
+msgstr "Оплатити &nbsp; %1$s"
#: src/cta/Payment/views.tsx:360
#, c-format
msgid "You have no balance for this currency. Withdraw digital cash first."
-msgstr ""
+msgstr "У вас немає балансу в цій валюті. Спочатку зніміть електронні гроші."
#: src/cta/Payment/views.tsx:364
#, c-format
@@ -891,231 +906,235 @@ msgid ""
"Could not find enough coins to pay. Even if you have enough %1$s some "
"restriction may apply."
msgstr ""
+"Не вдалося знайти достатню кількість монет для оплати. Навіть якщо у вас "
+"достатньо %1$s, можуть застосовуватися певні обмеження."
#: src/cta/Payment/views.tsx:366
#, c-format
msgid "Your current balance is not enough."
-msgstr ""
+msgstr "Ваш поточний баланс недостатній."
#: src/cta/Payment/views.tsx:395
#, c-format
msgid "Merchant message"
-msgstr ""
+msgstr "Повідомлення продавця"
#: src/cta/Refund/views.tsx:34
#, c-format
msgid "Could not load refund status"
-msgstr ""
+msgstr "Не вдалося завантажити статус повернення"
#: src/cta/Refund/views.tsx:48
#, c-format
msgid "Digital cash refund"
-msgstr ""
+msgstr "Повернення електронних грошей"
#: src/cta/Refund/views.tsx:52
#, c-format
msgid "You&apos;ve ignored the tip."
-msgstr ""
+msgstr "Ви проігнорували чайові."
#: src/cta/Refund/views.tsx:70
#, c-format
msgid "The refund is in progress."
-msgstr ""
+msgstr "Повернення коштів в процесі."
#: src/cta/Refund/views.tsx:76
#, c-format
msgid "Total to refund"
-msgstr ""
+msgstr "Загальна сума до повернення"
#: src/cta/Refund/views.tsx:106
#, c-format
msgid "The merchant &quot;%1$s&quot; is offering you a refund."
-msgstr ""
+msgstr "Продавець &quot;%1$s&quot; пропонує вам повернення коштів."
#: src/cta/Refund/views.tsx:115
#, c-format
msgid "Order amount"
-msgstr ""
+msgstr "Сума замовлення"
#: src/cta/Refund/views.tsx:122
#, c-format
msgid "Already refunded"
-msgstr ""
+msgstr "Вже повернено"
#: src/cta/Refund/views.tsx:129
#, c-format
msgid "Refund offered"
-msgstr ""
+msgstr "Повернення запропоновано"
#: src/cta/Refund/views.tsx:145
#, c-format
msgid "Accept &nbsp; %1$s"
-msgstr ""
+msgstr "Прийняти &nbsp; %1$s"
#: src/cta/Tip/views.tsx:32
#, c-format
msgid "Could not load tip status"
-msgstr ""
+msgstr "Не вдалося завантажити статус чайових"
#: src/cta/Tip/views.tsx:45
#, c-format
msgid "Digital cash tip"
-msgstr ""
+msgstr "Електронні гроші за чайові"
#: src/cta/Tip/views.tsx:66
#, c-format
msgid "The merchant is offering you a tip"
-msgstr ""
+msgstr "Продавець пропонує вам чайові"
#: src/cta/Tip/views.tsx:74
#, c-format
msgid "Merchant URL"
-msgstr ""
+msgstr "URL продавця"
#: src/cta/Tip/views.tsx:90
#, c-format
msgid "Receive &nbsp; %1$s"
-msgstr ""
+msgstr "Отримати &nbsp; %1$s"
#: src/cta/Tip/views.tsx:114
#, c-format
msgid "Tip from %1$s accepted. Check your transactions list for more details."
msgstr ""
+"Чайові від %1$s прийнято. Перевірте свій список транзакцій для отримання "
+"додаткової інформації."
#: src/components/SelectList.tsx:66
#, c-format
msgid "Select one option"
-msgstr ""
+msgstr "Виберіть один варіант"
#: src/components/TermsOfService/views.tsx:39
#, c-format
msgid "Could not load"
-msgstr ""
+msgstr "Не вдалося завантажити"
#: src/components/TermsOfService/views.tsx:73
#, c-format
msgid "Show terms of service"
-msgstr ""
+msgstr "Показати умови отримання послуг"
#: src/components/TermsOfService/views.tsx:81
#, c-format
msgid "I accept the exchange terms of service"
-msgstr ""
+msgstr "Я приймаю Умови надання послуг, цього обмінника"
#: src/components/TermsOfService/views.tsx:107
#, c-format
msgid "Exchange doesn&apos;t have terms of service"
-msgstr ""
+msgstr "Обмінник не має умов надання послуг"
#: src/components/TermsOfService/views.tsx:135
#, c-format
msgid "Review exchange terms of service"
-msgstr ""
+msgstr "Переглянути умови надання послуг обмінника"
#: src/components/TermsOfService/views.tsx:146
#, c-format
msgid "Review new version of terms of service"
-msgstr ""
+msgstr "Переглянути нову версію Умов надання послуг"
#: src/components/TermsOfService/views.tsx:170
#, c-format
msgid "The exchange reply with a empty terms of service"
-msgstr ""
+msgstr "Обмінник відповів порожніми умовами надання послуг"
#: src/components/TermsOfService/views.tsx:193
#, c-format
msgid "Download Terms of Service"
-msgstr ""
+msgstr "Завантажити Умови надання послуг"
#: src/components/TermsOfService/views.tsx:204
#, c-format
msgid "Hide terms of service"
-msgstr ""
+msgstr "Сховати Умови надання послуг"
#: src/wallet/ExchangeSelection/views.tsx:117
#, c-format
msgid "Could not load exchange fees"
-msgstr ""
+msgstr "Не вдалося завантажити комісії обмінника"
#: src/wallet/ExchangeSelection/views.tsx:131
#, c-format
msgid "Close"
-msgstr ""
+msgstr "Закрити"
#: src/wallet/ExchangeSelection/views.tsx:160
#, c-format
msgid "could not find any exchange"
-msgstr ""
+msgstr "не вдалося знайти жодного обмінника"
#: src/wallet/ExchangeSelection/views.tsx:166
#, c-format
msgid "could not find any exchange for the currency %1$s"
-msgstr ""
+msgstr "не вдалося знайти жодного обмінника для валюти %1$s"
#: src/wallet/ExchangeSelection/views.tsx:186
#, c-format
msgid "Service fee description"
-msgstr ""
+msgstr "Опис комісії за послуги"
#: src/wallet/ExchangeSelection/views.tsx:201
#, c-format
msgid "Select %1$s exchange"
-msgstr ""
+msgstr "Вибрати обмінник %1$s"
#: src/wallet/ExchangeSelection/views.tsx:215
#, c-format
msgid "Reset"
-msgstr ""
+msgstr "Скинути"
#: src/wallet/ExchangeSelection/views.tsx:218
#, c-format
msgid "Use this exchange"
-msgstr ""
+msgstr "Використовувати цей обмінник"
#: src/wallet/ExchangeSelection/views.tsx:230
#, c-format
msgid "Doesn&apos;t have auditors"
-msgstr ""
+msgstr "Немає аудиторів"
#: src/wallet/ExchangeSelection/views.tsx:241
#, c-format
msgid "currency"
-msgstr ""
+msgstr "валюта"
#: src/wallet/ExchangeSelection/views.tsx:249
#, c-format
msgid "Operations"
-msgstr ""
+msgstr "Операції"
#: src/wallet/ExchangeSelection/views.tsx:252
#, c-format
msgid "Deposits"
-msgstr ""
+msgstr "Депозити"
#: src/wallet/ExchangeSelection/views.tsx:259
#, c-format
msgid "Denomination"
-msgstr ""
+msgstr "Номінал"
#: src/wallet/ExchangeSelection/views.tsx:265
#, c-format
msgid "Until"
-msgstr ""
+msgstr "До"
#: src/wallet/ExchangeSelection/views.tsx:274
#, c-format
msgid "Withdrawals"
-msgstr ""
+msgstr "Зняття коштів"
#: src/wallet/ExchangeSelection/views.tsx:423
#, c-format
msgid "Currency"
-msgstr ""
+msgstr "Валюта"
#: src/wallet/ExchangeSelection/views.tsx:433
#, c-format
msgid "Coin operations"
-msgstr ""
+msgstr "Операції з монетами"
#: src/wallet/ExchangeSelection/views.tsx:436
#, c-format
@@ -1124,11 +1143,14 @@ msgid ""
"valid for a period of time. The exchange will charge the indicated amount every "
"time a coin is used in such operation."
msgstr ""
+"Кожна операція в цьому розділі може відрізнятися за номіналом і дійсна "
+"протягом певного періоду часу. Обмінник стягуватиме зазначену суму кожного "
+"разу, коли монета використовується в такій операції."
#: src/wallet/ExchangeSelection/views.tsx:545
#, c-format
msgid "Transfer operations"
-msgstr ""
+msgstr "Операції переказів"
#: src/wallet/ExchangeSelection/views.tsx:548
#, c-format
@@ -1137,96 +1159,99 @@ msgid ""
"for a period of time. The exchange will charge the indicated amount every time a "
"transfer is made."
msgstr ""
+"Кожна операція в цьому розділі може відрізнятися за типом переказу і дійсна "
+"протягом певного періоду часу. Обмінник стягуватиме зазначену суму кожного "
+"разу, коли здійснюється переказ."
#: src/wallet/ExchangeSelection/views.tsx:563
#, c-format
msgid "Operation"
-msgstr ""
+msgstr "Операція"
#: src/wallet/ExchangeSelection/views.tsx:583
#, c-format
msgid "Wallet operations"
-msgstr ""
+msgstr "Операції гаманця"
#: src/wallet/ExchangeSelection/views.tsx:597
#, c-format
msgid "Feature"
-msgstr ""
+msgstr "Функція"
#: src/cta/Withdraw/views.tsx:47
#, c-format
msgid "Could not get the info from the URI"
-msgstr ""
+msgstr "Не вдалося отримати інформацію з URI"
#: src/cta/Withdraw/views.tsx:60
#, c-format
msgid "Could not get info of withdrawal"
-msgstr ""
+msgstr "Не вдалося отримати інформацію про зняття коштів"
#: src/cta/Withdraw/views.tsx:74
#, c-format
msgid "Digital cash withdrawal"
-msgstr ""
+msgstr "Зняття електронних грошей"
#: src/cta/Withdraw/views.tsx:79
#, c-format
msgid "Could not finish the withdrawal operation"
-msgstr ""
+msgstr "Не вдалося завершити операцію зняття коштів"
#: src/cta/Withdraw/views.tsx:127
#, c-format
msgid "Age restriction"
-msgstr ""
+msgstr "Обмеження за віком"
#: src/cta/Withdraw/views.tsx:145
#, c-format
msgid "Withdraw &nbsp; %1$s"
-msgstr ""
+msgstr "Зняти &nbsp; %1$s"
#: src/cta/Withdraw/views.tsx:179
#, c-format
msgid "Withdraw to a mobile phone"
-msgstr ""
+msgstr "Зняти на мобільний телефон"
#: src/cta/InvoiceCreate/views.tsx:65
#, c-format
msgid "Digital invoice"
-msgstr ""
+msgstr "Електронний рахунок-фактура"
#: src/cta/InvoiceCreate/views.tsx:69
#, c-format
msgid "Could not finish the invoice creation"
-msgstr ""
+msgstr "Не вдалося завершити створення рахунку"
#: src/cta/InvoiceCreate/views.tsx:130
#, c-format
msgid "Create"
-msgstr ""
+msgstr "Створити"
#: src/cta/InvoicePay/views.tsx:63
#, c-format
msgid "Could not finish the payment operation"
-msgstr ""
+msgstr "Не вдалося завершити операцію оплати"
#: src/cta/TransferCreate/views.tsx:55
#, c-format
msgid "Digital cash transfer"
-msgstr ""
+msgstr "Переказ електронних грошей"
#: src/cta/TransferCreate/views.tsx:59
#, c-format
msgid "Could not finish the transfer creation"
-msgstr ""
+msgstr "Не вдалося завершити створення переказу"
#: src/cta/TransferPickup/views.tsx:57
#, c-format
msgid "Could not finish the pickup operation"
-msgstr ""
+msgstr "Не вдалося завершити операцію отримання"
#: src/wallet/CreateManualWithdraw.tsx:149
#, c-format
msgid "Manual Withdrawal for %1$s"
-msgstr ""
+msgstr "Ручне зняття для %1$s"
#: src/wallet/CreateManualWithdraw.tsx:154
#, c-format
@@ -1235,271 +1260,277 @@ msgid ""
"the coins to this wallet after receiving a wire transfer with the correct "
"subject."
msgstr ""
+"Виберіть обмінник, з якого будуть зняті монети. Обмінник надішле монети в "
+"цей гаманець після отримання банківського переказу з правильним призначенням."
#: src/wallet/CreateManualWithdraw.tsx:162
#, c-format
msgid "No exchange found for %1$s"
-msgstr ""
+msgstr "Не знайдено обмінника для %1$s"
#: src/wallet/CreateManualWithdraw.tsx:170
#, c-format
msgid "Add Exchange"
-msgstr ""
+msgstr "Додати обмінник"
#: src/wallet/CreateManualWithdraw.tsx:192
#, c-format
msgid "No exchange configured"
-msgstr ""
+msgstr "Обмінник не налаштовано"
#: src/wallet/CreateManualWithdraw.tsx:210
#, c-format
msgid "Can&apos;t create the reserve"
-msgstr ""
+msgstr "Не вдається створити резерв"
#: src/wallet/CreateManualWithdraw.tsx:277
#, c-format
msgid "Start withdrawal"
-msgstr ""
+msgstr "Розпочати зняття коштів"
#: src/wallet/DepositPage/views.tsx:38
#, c-format
msgid "Could not load deposit balance"
-msgstr ""
+msgstr "Не вдалося завантажити баланс депозиту"
#: src/wallet/DepositPage/views.tsx:51
#, c-format
msgid "A currency or an amount should be indicated"
-msgstr ""
+msgstr "Необхідно вказати валюту або суму"
#: src/wallet/DepositPage/views.tsx:67
#, c-format
msgid "There is no enough balance to make a deposit for currency %1$s"
-msgstr ""
+msgstr "Недостатньо балансу для внесення депозиту у валюті %1$s"
#: src/wallet/DepositPage/views.tsx:117
#, c-format
msgid "Send %1$s to your account"
-msgstr ""
+msgstr "Надіслати %1$s на ваш рахунок"
#: src/wallet/DepositPage/views.tsx:121
#, c-format
msgid "There is no account to make a deposit for currency %1$s"
-msgstr ""
+msgstr "Немає рахунку для внесення депозиту у валюті %1$s"
#: src/wallet/DepositPage/views.tsx:127
#, c-format
msgid "Add account"
-msgstr ""
+msgstr "Додати рахунок"
#: src/wallet/DepositPage/views.tsx:151
#, c-format
msgid "Select account"
-msgstr ""
+msgstr "Вибрати рахунок"
#: src/wallet/DepositPage/views.tsx:163
#, c-format
msgid "Add another account"
-msgstr ""
+msgstr "Додати інший рахунок"
#: src/wallet/DepositPage/views.tsx:191
#, c-format
msgid "Deposit fee"
-msgstr ""
+msgstr "Комісія за депозит"
#: src/wallet/DepositPage/views.tsx:205
#, c-format
msgid "Total deposit"
-msgstr ""
+msgstr "Загальний депозит"
#: src/wallet/DepositPage/views.tsx:233
#, c-format
msgid "Deposit&nbsp;%1$s %2$s"
-msgstr ""
+msgstr "Депозит&nbsp;%1$s %2$s"
#: src/wallet/AddAccount/views.tsx:56
#, c-format
msgid "Add bank account for %1$s"
-msgstr ""
+msgstr "Додати банківський рахунок для %1$s"
#: src/wallet/AddAccount/views.tsx:59
#, c-format
msgid "Enter the URL of an exchange you trust."
-msgstr ""
+msgstr "Введіть URL обмінника, якому ви довіряєте."
#: src/wallet/AddAccount/views.tsx:66
#, c-format
msgid "Unable add this account"
-msgstr ""
+msgstr "Не вдалося додати цей рахунок"
#: src/wallet/AddAccount/views.tsx:73
#, c-format
msgid "Select account type"
-msgstr ""
+msgstr "Виберіть тип рахунку"
#: src/wallet/ExchangeAddConfirm.tsx:42
#, c-format
msgid "Review terms of service"
-msgstr ""
+msgstr "Перегляньте умови обслуговування"
#: src/wallet/ExchangeAddConfirm.tsx:45
#, c-format
msgid "Exchange URL"
-msgstr ""
+msgstr "URL обмінника"
#: src/wallet/ExchangeAddConfirm.tsx:70
#, c-format
msgid "Add exchange"
-msgstr ""
+msgstr "Додати обмінник"
#: src/wallet/ExchangeSetUrl.tsx:112
#, c-format
msgid "Add new exchange"
-msgstr ""
+msgstr "Додати новий обмінник"
#: src/wallet/ExchangeSetUrl.tsx:116
#, c-format
msgid "Add exchange for %1$s"
-msgstr ""
+msgstr "Додати обмінник для %1$s"
#: src/wallet/ExchangeSetUrl.tsx:128
#, c-format
msgid "An exchange has been found! Review the information and click next"
-msgstr ""
+msgstr "Знайдено обмінник! Перегляньте інформацію та натисніть \"Далі\""
#: src/wallet/ExchangeSetUrl.tsx:135
#, c-format
msgid "This exchange doesn&apos;t match the expected currency %1$s"
-msgstr ""
+msgstr "Цей обмінник не відповідає очікуваній валюті %1$s"
#: src/wallet/ExchangeSetUrl.tsx:143
#, c-format
msgid "Unable to verify this exchange"
-msgstr ""
+msgstr "Не вдалося перевірити цей обмінник"
#: src/wallet/ExchangeSetUrl.tsx:151
#, c-format
msgid "Unable to add this exchange"
-msgstr ""
+msgstr "Не вдалося додати цей обмінник"
#: src/wallet/ExchangeSetUrl.tsx:167
#, c-format
msgid "loading"
-msgstr ""
+msgstr "завантаження"
#: src/wallet/ExchangeSetUrl.tsx:174
#, c-format
msgid "Version"
-msgstr ""
+msgstr "Версія"
#: src/wallet/ExchangeSetUrl.tsx:206
#, c-format
msgid "Next"
-msgstr ""
+msgstr "Далі"
#: src/components/TransactionItem.tsx:201
#, c-format
msgid "Waiting for confirmation"
-msgstr ""
+msgstr "Очікування підтвердження"
#: src/components/TransactionItem.tsx:266
#, c-format
msgid "PENDING"
-msgstr ""
+msgstr "В ОБРОБЦІ"
#: src/wallet/History.tsx:75
#, c-format
msgid "Could not load the list of transactions"
-msgstr ""
+msgstr "Не вдалося завантажити список транзакцій"
#: src/wallet/History.tsx:233
#, c-format
msgid "Your transaction history is empty for this currency."
-msgstr ""
+msgstr "Ваша історія транзакцій порожня для цієї валюти."
#: src/wallet/ProviderAddPage.tsx:127
#, c-format
msgid "Add backup provider"
-msgstr ""
+msgstr "Додати постачальника резервного копіювання"
#: src/wallet/ProviderAddPage.tsx:131
#, c-format
msgid "Could not get provider information"
-msgstr ""
+msgstr "Не вдалося отримати інформацію про постачальника"
#: src/wallet/ProviderAddPage.tsx:140
#, c-format
msgid "Backup providers may charge for their service"
msgstr ""
+"Постачальники резервного копіювання можуть стягувати плату за свої послуги"
#: src/wallet/ProviderAddPage.tsx:147
#, c-format
msgid "URL"
-msgstr ""
+msgstr "URL"
#: src/wallet/ProviderAddPage.tsx:158
#, c-format
msgid "Name"
-msgstr ""
+msgstr "Назва"
#: src/wallet/ProviderAddPage.tsx:212
#, c-format
msgid "Provider URL"
-msgstr ""
+msgstr "URL постачальника"
#: src/wallet/ProviderAddPage.tsx:218
#, c-format
msgid "Please review and accept this provider&apos;s terms of service"
msgstr ""
+"Будь ласка, перегляньте та прийміть Умови надання послуг цього постачальника"
#: src/wallet/ProviderAddPage.tsx:223
#, c-format
msgid "Pricing"
-msgstr ""
+msgstr "Ціни"
#: src/wallet/ProviderAddPage.tsx:226
#, c-format
msgid "free of charge"
-msgstr ""
+msgstr "безкоштовно"
#: src/wallet/ProviderAddPage.tsx:228
#, c-format
msgid "%1$s per year of service"
-msgstr ""
+msgstr "%1$s на рік обслуговування"
#: src/wallet/ProviderAddPage.tsx:235
#, c-format
msgid "Storage"
-msgstr ""
+msgstr "Сховище"
#: src/wallet/ProviderAddPage.tsx:238
#, c-format
msgid "%1$s megabytes of storage per year of service"
-msgstr ""
+msgstr "%1$s мегабайт сховища на рік обслуговування"
#: src/wallet/ProviderAddPage.tsx:244
#, c-format
msgid "Accept terms of service"
-msgstr ""
+msgstr "Прийняти умови надання послуг"
#: src/wallet/ReserveCreated.tsx:44
#, c-format
msgid "Could not parse the payto URI"
-msgstr ""
+msgstr "Не вдалося розібрати URI для оплати"
#: src/wallet/ReserveCreated.tsx:45
#, c-format
msgid "Please check the uri"
-msgstr ""
+msgstr "Будь ласка, перевірте URI"
#: src/wallet/ReserveCreated.tsx:75
#, c-format
msgid "Exchange is ready for withdrawal"
-msgstr ""
+msgstr "Обмінник готовий до зняття коштів"
#: src/wallet/ReserveCreated.tsx:78
#, c-format
msgid "To complete the process you need to wire%1$s %2$s to the exchange bank account"
msgstr ""
+"Щоб завершити процес, вам потрібно переказати %1$s %2$s на банківський "
+"рахунок обмінника"
#: src/wallet/ReserveCreated.tsx:87
#, c-format
@@ -1507,31 +1538,33 @@ msgid ""
"Alternative, you can also scan this QR code or open %1$s if you have a banking "
"app installed that supports RFC 8905"
msgstr ""
+"Або ви можете відсканувати цей QR-код або відкрити %1$s, якщо у вас "
+"встановлений банківський додаток, який підтримує RFC 8905"
#: src/wallet/ReserveCreated.tsx:98
#, c-format
msgid "Cancel withdrawal"
-msgstr ""
+msgstr "Скасувати зняття"
#: src/wallet/Settings.tsx:115
#, c-format
msgid "Could not toggle auto-open"
-msgstr ""
+msgstr "Не вдалося перемкнути автоматичне відкриття"
#: src/wallet/Settings.tsx:121
#, c-format
msgid "Could not toggle clipboard"
-msgstr ""
+msgstr "Не вдалося перемкнути буфер обміну"
#: src/wallet/Settings.tsx:126
#, c-format
msgid "Navigator"
-msgstr ""
+msgstr "Навігатор"
#: src/wallet/Settings.tsx:129
#, c-format
msgid "Automatically open wallet based on page content"
-msgstr ""
+msgstr "Автоматично відкривати гаманець на основі вмісту сторінки"
#: src/wallet/Settings.tsx:135
#, c-format
@@ -1539,111 +1572,113 @@ msgid ""
"Enabling this option below will make using the wallet faster, but requires more "
"permissions from your browser."
msgstr ""
+"Увімкнення цієї опції зробить використання гаманця швидшим, але вимагає "
+"більше дозволів від вашого браузера."
#: src/wallet/Settings.tsx:145
#, c-format
msgid "Automatically check clipboard for Taler URI"
-msgstr ""
+msgstr "Автоматично перевіряти буфер обміну на наявність Taler URI"
#: src/wallet/Settings.tsx:162
#, c-format
msgid "Trust"
-msgstr ""
+msgstr "Довіряти"
#: src/wallet/Settings.tsx:166
#, c-format
msgid "No exchange yet"
-msgstr ""
+msgstr "Ще немає обмінника"
#: src/wallet/Settings.tsx:180
#, c-format
msgid "Term of Service"
-msgstr ""
+msgstr "Умови обслуговування"
#: src/wallet/Settings.tsx:191
#, c-format
msgid "ok"
-msgstr ""
+msgstr "ок"
#: src/wallet/Settings.tsx:197
#, c-format
msgid "changed"
-msgstr ""
+msgstr "змінено"
#: src/wallet/Settings.tsx:204
#, c-format
msgid "not accepted"
-msgstr ""
+msgstr "не прийнято"
#: src/wallet/Settings.tsx:210
#, c-format
msgid "unknown (exchange status should be updated)"
-msgstr ""
+msgstr "невідомо (статус обмінника має бути оновлено)"
#: src/wallet/Settings.tsx:236
#, c-format
msgid "Add an exchange"
-msgstr ""
+msgstr "Додати обмінник"
#: src/wallet/Settings.tsx:241
#, c-format
msgid "Troubleshooting"
-msgstr ""
+msgstr "Вирішення проблем"
#: src/wallet/Settings.tsx:244
#, c-format
msgid "Developer mode"
-msgstr ""
+msgstr "Режим розробника"
#: src/wallet/Settings.tsx:246
#, c-format
msgid "More options and information useful for debugging"
-msgstr ""
+msgstr "Більше опцій та інформації, корисної для відлагодження"
#: src/wallet/Settings.tsx:257
#, c-format
msgid "Display"
-msgstr ""
+msgstr "Дисплей"
#: src/wallet/Settings.tsx:261
#, c-format
msgid "Current Language"
-msgstr ""
+msgstr "Поточна мова"
#: src/wallet/Settings.tsx:274
#, c-format
msgid "Wallet Core"
-msgstr ""
+msgstr "Ядро гаманця"
#: src/wallet/Settings.tsx:284
#, c-format
msgid "Web Extension"
-msgstr ""
+msgstr "Веб-розширення"
#: src/wallet/Settings.tsx:295
#, c-format
msgid "Exchange compatibility"
-msgstr ""
+msgstr "Сумісність з обмінником"
#: src/wallet/Settings.tsx:299
#, c-format
msgid "Merchant compatibility"
-msgstr ""
+msgstr "Сумісність з продавцем"
#: src/wallet/Settings.tsx:303
#, c-format
msgid "Bank compatibility"
-msgstr ""
+msgstr "Сумісність з банком"
#: src/wallet/Welcome.tsx:59
#, c-format
msgid "Browser Extension Installed!"
-msgstr ""
+msgstr "Розширення для браузера встановлено!"
#: src/wallet/Welcome.tsx:63
#, c-format
msgid "You can open the GNU Taler Wallet using the combination %1$s ."
-msgstr ""
+msgstr "Ви можете відкрити гаманець GNU Taler за допомогою комбінації %1$s."
#: src/wallet/Welcome.tsx:72
#, c-format
@@ -1651,26 +1686,28 @@ msgid ""
"Also pinning the GNU Taler Wallet to your Chrome browser allows you to quick "
"access without keyboard:"
msgstr ""
+"Також закріплення гаманця GNU Taler у вашому браузері Chrome дозволяє швидко "
+"отримати доступ без клавіатури:"
#: src/wallet/Welcome.tsx:79
#, c-format
msgid "Click the puzzle icon"
-msgstr ""
+msgstr "Натисніть на іконку пазлу"
#: src/wallet/Welcome.tsx:82
#, c-format
msgid "Search for GNU Taler Wallet"
-msgstr ""
+msgstr "Знайдіть гаманець GNU Taler"
#: src/wallet/Welcome.tsx:85
#, c-format
msgid "Click the pin icon"
-msgstr ""
+msgstr "Натисніть на іконку шпильки"
#: src/wallet/Welcome.tsx:91
#, c-format
msgid "Permissions"
-msgstr ""
+msgstr "Дозволи"
#: src/wallet/Welcome.tsx:100
#, c-format
@@ -1678,31 +1715,33 @@ msgid ""
"(Enabling this option below will make using the wallet faster, but requires more "
"permissions from your browser.)"
msgstr ""
+"(Увімкнення цієї опції нижче зробить використання гаманця швидшим, але "
+"вимагає більше дозволів від вашого браузера.)"
#: src/wallet/Welcome.tsx:110
#, c-format
msgid "Next Steps"
-msgstr ""
+msgstr "Наступні кроки"
#: src/wallet/Welcome.tsx:113
#, c-format
msgid "Try the demo"
-msgstr ""
+msgstr "Спробуйте демо"
#: src/wallet/Welcome.tsx:116
#, c-format
msgid "Learn how to top up your wallet balance"
-msgstr ""
+msgstr "Дізнайтеся, як поповнити баланс вашого гаманця"
#: src/components/Diagnostics.tsx:31
#, c-format
msgid "Diagnostics timed out. Could not talk to the wallet backend."
-msgstr ""
+msgstr "Час діагностики вичерпано. Не вдалося зв'язатися з бекендом гаманця."
#: src/components/Diagnostics.tsx:52
#, c-format
msgid "Problems detected:"
-msgstr ""
+msgstr "Виявлено проблеми:"
#: src/components/Diagnostics.tsx:61
#, c-format
@@ -1710,6 +1749,8 @@ msgid ""
"Please check in your %1$s settings that you have IndexedDB enabled (check the "
"preference name %2$s)."
msgstr ""
+"Будь ласка, перевірте у налаштуваннях %1$s, що у вас увімкнено IndexedDB ("
+"перевірте назву налаштування %2$s)."
#: src/components/Diagnostics.tsx:70
#, c-format
@@ -1717,16 +1758,18 @@ msgid ""
"Your wallet database is outdated. Currently automatic migration is not "
"supported. Please go %1$s to reset the wallet database."
msgstr ""
+"База даних вашого гаманця застаріла. Наразі автоматична міграція не "
+"підтримується. Будь ласка, перейдіть до %1$s, щоб скинути базу даних гаманця."
#: src/components/Diagnostics.tsx:83
#, c-format
msgid "Running diagnostics"
-msgstr ""
+msgstr "Запуск діагностики"
#: src/wallet/DeveloperPage.tsx:163
#, c-format
msgid "Debug tools"
-msgstr ""
+msgstr "Інструменти відлагодження"
#: src/wallet/DeveloperPage.tsx:170
#, c-format
@@ -1734,223 +1777,225 @@ msgid ""
"Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL "
"YOUR COINS?"
msgstr ""
+"Ви хочете НЕВІДВОРОТНО ЗНИЩИТИ все всередині вашого гаманця і ВТРАТИТИ ВСІ "
+"СВОЇ МОНЕТИ?"
#: src/wallet/DeveloperPage.tsx:176
#, c-format
msgid "reset"
-msgstr ""
+msgstr "скинути"
#: src/wallet/DeveloperPage.tsx:183
#, c-format
msgid "TESTING: This may delete all your coin, proceed with caution"
-msgstr ""
+msgstr "ТЕСТУВАННЯ: Це може видалити всі ваші монети, будьте обережні"
#: src/wallet/DeveloperPage.tsx:189
#, c-format
msgid "run gc"
-msgstr ""
+msgstr "запустити gc"
#: src/wallet/DeveloperPage.tsx:197
#, c-format
msgid "import database"
-msgstr ""
+msgstr "імпортувати базу даних"
#: src/wallet/DeveloperPage.tsx:219
#, c-format
msgid "export database"
-msgstr ""
+msgstr "експортувати базу даних"
#: src/wallet/DeveloperPage.tsx:225
#, c-format
msgid "Database exported at %1$s %2$s to download"
-msgstr ""
+msgstr "База даних експортована о %1$s %2$s для завантаження"
#: src/wallet/DeveloperPage.tsx:248
#, c-format
msgid "Coins"
-msgstr ""
+msgstr "Монети"
#: src/wallet/DeveloperPage.tsx:282
#, c-format
msgid "Pending operations"
-msgstr ""
+msgstr "Очікувані операції"
#: src/wallet/DeveloperPage.tsx:328
#, c-format
msgid "usable coins"
-msgstr ""
+msgstr "використовувані монети"
#: src/wallet/DeveloperPage.tsx:337
#, c-format
msgid "id"
-msgstr ""
+msgstr "ідентифікатор"
#: src/wallet/DeveloperPage.tsx:340
#, c-format
msgid "denom"
-msgstr ""
+msgstr "номінал"
#: src/wallet/DeveloperPage.tsx:343
#, c-format
msgid "value"
-msgstr ""
+msgstr "значення"
#: src/wallet/DeveloperPage.tsx:346
#, c-format
msgid "status"
-msgstr ""
+msgstr "статус"
#: src/wallet/DeveloperPage.tsx:349
#, c-format
msgid "from refresh?"
-msgstr ""
+msgstr "з оновлення?"
#: src/wallet/DeveloperPage.tsx:352
#, c-format
msgid "age key count"
-msgstr ""
+msgstr "кількість ключів за віком"
#: src/wallet/DeveloperPage.tsx:369
#, c-format
msgid "spent coins"
-msgstr ""
+msgstr "витрачені монети"
#: src/wallet/DeveloperPage.tsx:373
#, c-format
msgid "click to show"
-msgstr ""
+msgstr "натисніть, щоб показати"
#: src/wallet/QrReader.tsx:108
#, c-format
msgid "Scan a QR code or enter taler:// URI below"
-msgstr ""
+msgstr "Скануйте QR-код або введіть taler:// URI нижче"
#: src/wallet/QrReader.tsx:122
#, c-format
msgid "Open"
-msgstr "Доступні"
+msgstr "Відкрити"
#: src/wallet/QrReader.tsx:128
#, c-format
msgid "URI is not valid. Taler URI should start with `taler://`"
-msgstr ""
+msgstr "URI недійсний. Taler URI повинен починатися з `taler://`"
#: src/wallet/QrReader.tsx:133
#, c-format
msgid "Try another"
-msgstr ""
+msgstr "Спробуйте інший"
#: src/wallet/DestinationSelection.tsx:183
#, c-format
msgid "Could not load list of exchange"
-msgstr ""
+msgstr "Не вдалося завантажити список обмінників"
#: src/wallet/DestinationSelection.tsx:209
#, c-format
msgid "Choose a currency to proceed or add another exchange"
-msgstr ""
+msgstr "Виберіть валюту для продовження або додайте інший обмінник"
#: src/wallet/DestinationSelection.tsx:217
#, c-format
msgid "Known currencies"
-msgstr ""
+msgstr "Відомі валюти"
#: src/wallet/DestinationSelection.tsx:318
#, c-format
msgid "Specify the amount and the origin"
-msgstr ""
+msgstr "Вкажіть суму та джерело"
#: src/wallet/DestinationSelection.tsx:336
#, c-format
msgid "Change currency"
-msgstr ""
+msgstr "Змінити валюту"
#: src/wallet/DestinationSelection.tsx:344
#, c-format
msgid "Use previous origins:"
-msgstr ""
+msgstr "Використовувати попередні джерела:"
#: src/wallet/DestinationSelection.tsx:364
#, c-format
msgid "Or specify the origin of the money"
-msgstr ""
+msgstr "Або вкажіть джерело грошей"
#: src/wallet/DestinationSelection.tsx:372
#, c-format
msgid "Specify the origin of the money"
-msgstr ""
+msgstr "Вкажіть джерело грошей"
#: src/wallet/DestinationSelection.tsx:380
#, c-format
msgid "From my bank account"
-msgstr ""
+msgstr "З мого банківського рахунку"
#: src/wallet/DestinationSelection.tsx:395
#, c-format
msgid "From another wallet"
-msgstr ""
+msgstr "З іншого гаманця"
#: src/wallet/DestinationSelection.tsx:449
#, c-format
msgid "currency not provided"
-msgstr ""
+msgstr "валюта не вказана"
#: src/wallet/DestinationSelection.tsx:459
#, c-format
msgid "Specify the amount and the destination"
-msgstr ""
+msgstr "Вкажіть суму та місце призначення"
#: src/wallet/DestinationSelection.tsx:483
#, c-format
msgid "Use previous destinations:"
-msgstr ""
+msgstr "Використовувати попередні місця призначення:"
#: src/wallet/DestinationSelection.tsx:503
#, c-format
msgid "Or specify the destination of the money"
-msgstr ""
+msgstr "Або вкажіть місце призначення грошей"
#: src/wallet/DestinationSelection.tsx:511
#, c-format
msgid "Specify the destination of the money"
-msgstr ""
+msgstr "Вкажіть адрес призначення грошей"
#: src/wallet/DestinationSelection.tsx:521
#, c-format
msgid "To my bank account"
-msgstr ""
+msgstr "На мій банківський рахунок"
#: src/wallet/DestinationSelection.tsx:534
#, c-format
msgid "To another wallet"
-msgstr ""
+msgstr "На інший гаманець"
#: src/cta/Recovery/views.tsx:30
#, c-format
msgid "Could not load backup recovery information"
-msgstr ""
+msgstr "Не вдалося завантажити інформацію для відновлення резервної копії"
#: src/cta/Recovery/views.tsx:47
#, c-format
msgid "Digital wallet recovery"
-msgstr ""
+msgstr "Відновлення цифрового гаманця"
#: src/cta/Recovery/views.tsx:52
#, c-format
msgid "Import backup, show info"
-msgstr ""
+msgstr "Імпортувати резервну копію, показати інформацію"
#: src/wallet/Application.tsx:189
#, c-format
msgid "All done, your transaction is in progress"
-msgstr ""
+msgstr "Все готово, ваша транзакція в процесі"
#: src/components/EditableText.tsx:45
#, c-format
msgid "Edit"
-msgstr ""
+msgstr "Редагувати"
#: src/wallet/ManualWithdrawPage.tsx:102
#, c-format
msgid "Could not load the list of known exchanges"
-msgstr ""
+msgstr "Не вдалося завантажити список відомих обмінників"
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 276d464a0..3f6708fc0 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -35,6 +35,7 @@ import {
Settings,
defaultSettings,
} from "./api.js";
+import { encodeCrockForURI } from "@gnu-taler/web-util/browser";
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
isFirefox,
@@ -178,54 +179,54 @@ function openWalletURIFromPopup(uri: TalerUri): void {
case TalerUriAction.WithdrawExchange:
case TalerUriAction.Withdraw:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/withdraw?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/withdraw?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.Restore:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/recovery?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/recovery?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.Pay:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/pay?talerUri=${encodeURIComponent(talerUri)}`,
+ `static/wallet.html#/cta/pay?talerUri=${encodeCrockForURI(talerUri)}`,
);
break;
case TalerUriAction.Refund:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/refund?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/refund?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.PayPull:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/invoice/pay?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/invoice/pay?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.PayPush:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/transfer/pickup?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/transfer/pickup?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.PayTemplate:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/pay/template?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/pay/template?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
break;
case TalerUriAction.AddExchange:
url = chrome.runtime.getURL(
- `static/wallet.html#/cta/add/exchange?talerUri=${encodeURIComponent(
+ `static/wallet.html#/cta/add/exchange?talerUri=${encodeCrockForURI(
talerUri,
)}`,
);
diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts
index 3d67423fd..4292bd7fd 100644
--- a/packages/taler-wallet-webextension/src/platform/firefox.ts
+++ b/packages/taler-wallet-webextension/src/platform/firefox.ts
@@ -18,9 +18,8 @@ import {
BackgroundPlatformAPI,
CrossBrowserPermissionsApi,
ForegroundPlatformAPI,
- Permissions,
Settings,
- defaultSettings,
+ defaultSettings
} from "./api.js";
import chromePlatform, {
containsClipboardPermissions as chromeClipContains,
diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx
index cbb9b50b2..e971c4375 100644
--- a/packages/taler-wallet-webextension/src/popup/Application.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Application.tsx
@@ -20,21 +20,24 @@
* @author sebasjm
*/
+import { ScopeInfo, stringifyScopeInfoShort } from "@gnu-taler/taler-util";
import {
TranslationProvider,
useTranslationContext,
+ encodeCrockForURI,
+ decodeCrockFromURI,
} from "@gnu-taler/web-util/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, useState } from "preact/hooks";
+import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";
import PendingTransactions from "../components/PendingTransactions.js";
import { PopupBox } from "../components/styled/index.js";
import { AlertProvider } from "../context/alert.js";
import { IoCProviderForRuntime } from "../context/iocContext.js";
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";
@@ -59,7 +62,7 @@ function ApplicationView(): VNode {
useEffect(() => {
if (actionUri) {
- route(Pages.cta({ action: encodeURIComponent(actionUri) }));
+ route(Pages.cta({ action: encodeCrockForURI(actionUri) }));
}
}, [actionUri]);
@@ -68,7 +71,7 @@ function ApplicationView(): VNode {
}
function redirectToURL(str: string): void {
- platform.openNewURLFromPopup(new URL(str))
+ platform.openNewURLFromPopup(new URL(str));
}
return (
@@ -76,14 +79,34 @@ function ApplicationView(): VNode {
<Route
path={Pages.balance}
component={() => (
- <PopupTemplate path="balance" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <PopupTemplate
+ path="balance"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<BalancePage
- goToWalletManualWithdraw={() => redirectTo(Pages.receiveCash({}))}
- goToWalletDeposit={(currency: string) =>
- redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+ goToWalletManualWithdraw={(scope?: ScopeInfo) => {
+ return redirectTo(
+ Pages.receiveCash({
+ scope: !scope
+ ? undefined
+ : encodeCrockForURI(stringifyScopeInfoShort(scope)),
+ }),
+ );
+ }}
+ goToWalletDeposit={(scope: ScopeInfo) =>
+ redirectTo(
+ Pages.sendCash({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(scope)),
+ }),
+ )
}
- goToWalletHistory={(currency: string) =>
- redirectTo(Pages.balanceHistory({ currency }))
+ goToWalletHistory={(scope: ScopeInfo) =>
+ redirectTo(
+ Pages.balanceHistory({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(scope)),
+ }),
+ )
}
/>
</PopupTemplate>
@@ -98,7 +121,7 @@ function ApplicationView(): VNode {
return (
<PopupTemplate goToURL={redirectToURL}>
<TalerActionFound
- url={decodeURIComponent(action)}
+ url={decodeCrockFromURI(action)}
onDismiss={() => {
setDismissed(true);
return redirectTo(Pages.balance);
@@ -112,7 +135,11 @@ function ApplicationView(): VNode {
<Route
path={Pages.backup}
component={() => (
- <PopupTemplate path="backup" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <PopupTemplate
+ path="backup"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<BackupPage
onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
/>
@@ -127,9 +154,9 @@ function ApplicationView(): VNode {
onPayProvider={(uri: string) =>
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
}
- onWithdraw={(amount: string) =>
- redirectTo(Pages.receiveCash({ amount }))
- }
+ onWithdraw={async (_amount: string) => {
+ // redirectTo(Pages.receiveCash({ amount }))
+ }}
pid={pid}
onBack={() => redirectTo(Pages.backup)}
/>
@@ -219,7 +246,10 @@ function PopupTemplate({
}): VNode {
return (
<Fragment>
- <PendingTransactions goToTransaction={goToTransaction} goToURL={goToURL} />
+ <PendingTransactions
+ goToTransaction={goToTransaction}
+ goToURL={goToURL}
+ />
<PopupNavBar path={path} />
<PopupBox>
<AlertProvider>{children}</AlertProvider>
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 73bd8e96d..e730448ac 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -17,6 +17,7 @@
import {
Amounts,
NotificationType,
+ ScopeInfo,
WalletBalance,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -41,9 +42,9 @@ import { AddNewActionView } from "../wallet/AddNewActionView.js";
import { NoBalanceHelp } from "./NoBalanceHelp.js";
export interface Props {
- goToWalletDeposit: (currency: string) => Promise<void>;
- goToWalletHistory: (currency: string) => Promise<void>;
- goToWalletManualWithdraw: () => Promise<void>;
+ goToWalletDeposit: (scope: ScopeInfo) => Promise<void>;
+ goToWalletHistory: (scope: ScopeInfo) => Promise<void>;
+ goToWalletManualWithdraw: (scope?: ScopeInfo) => Promise<void>;
}
export type State = State.Loading | State.Error | State.Action | State.Balances;
@@ -70,8 +71,8 @@ export namespace State {
error: undefined;
balances: WalletBalance[];
addAction: ButtonHandler;
- goToWalletDeposit: (currency: string) => Promise<void>;
- goToWalletHistory: (currency: string) => Promise<void>;
+ goToWalletDeposit: (currency: ScopeInfo) => Promise<void>;
+ goToWalletHistory: (currency: ScopeInfo) => Promise<void>;
goToWalletManualWithdraw: ButtonHandler;
}
}
@@ -105,8 +106,7 @@ function useComponentState({
if (state.hasError) {
return {
status: "error",
- error: alertFromError( i18n,
- i18n.str`Could not load the balance`, state),
+ error: alertFromError(i18n, i18n.str`Could not load the balance`, state),
};
}
if (addingAction) {
@@ -126,7 +126,9 @@ function useComponentState({
onClick: pushAlertOnError(async () => setAddingAction(true)),
},
goToWalletManualWithdraw: {
- onClick: pushAlertOnError(goToWalletManualWithdraw),
+ onClick: pushAlertOnError(async () => {
+ goToWalletManualWithdraw(state.response.balances.length ? state.response.balances[0].scopeInfo : undefined);
+ }),
},
goToWalletDeposit,
goToWalletHistory,
@@ -155,8 +157,8 @@ export function BalanceView(state: State.Balances): VNode {
const currencyWithNonZeroAmount = state.balances
.filter((b) => !Amounts.isZero(b.available))
.map((b) => {
- b.flags
- return b.available.split(":")[0]
+ b.flags;
+ return b.scopeInfo;
});
if (state.balances.length === 0) {
@@ -172,7 +174,10 @@ export function BalanceView(state: State.Balances): VNode {
<section>
<BalanceTable
balances={state.balances}
- goToWalletHistory={state.goToWalletHistory}
+ goToWalletHistory={(e) => {
+ console.log("qwe", e);
+ state.goToWalletHistory(e);
+ }}
/>
</section>
<footer style={{ justifyContent: "space-between" }}>
@@ -184,7 +189,7 @@ export function BalanceView(state: State.Balances): VNode {
</Button>
{currencyWithNonZeroAmount.length > 0 && (
<MultiActionButton
- label={(s) => i18n.str`Send ${s}`}
+ label={(s) => i18n.str`Send ${s.currency}`}
actions={currencyWithNonZeroAmount}
onClick={(c) => state.goToWalletDeposit(c)}
/>
diff --git a/packages/taler-wallet-webextension/src/pwa/index.html b/packages/taler-wallet-webextension/src/pwa/index.html
index c150ee68d..da1bcc479 100644
--- a/packages/taler-wallet-webextension/src/pwa/index.html
+++ b/packages/taler-wallet-webextension/src/pwa/index.html
@@ -3,18 +3,56 @@
<meta charset="utf-8" />
<link rel="manifest" href="./manifest.json" />
<style>
+ /* Normalize font-family, rather than letting the UA decide */
+ html {
+ font-family:
+ system-ui,
+ -apple-system,
+ BlinkMacSystemFont,
+ Roboto,
+ Oxygen,
+ Ubuntu,
+ Cantarell,
+ "Open Sans",
+ "Helvetica Neue",
+ sans-serif;
+ }
+
+ /* Setup the popup overlay */
.overlay {
- position: absolute;
+ /* TODO: Consider moving it to the top right of the screen, like an actual popup usually is */
+ position: fixed;
top: 0px;
+ left: 0px;
display: none;
width: 100%;
height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
+ background-color: #0007;
+ backdrop-filter: blur(12px);
color: white;
justify-content: center;
}
.overlay > iframe {
margin: auto;
+ border: 1px solid #666;
+ }
+ #wallet-window {
+ border: 1px solid #666;
+ border-radius: 4px;
+ /* TODO: why arbitrary 38px? also why no flexbox? */
+ height: calc(100% - 38px);
+ width: min(850px, calc(100% - 8px));
+ }
+
+ /* firefox's native button styles more or less, because conistency is good */
+ button {
+ background: #e9e9ed;
+ color: #151516;
+ border: 1px solid #828282;
+ border-radius: 4px;
+ }
+ button:hover {
+ background: #c0c0c0;
}
</style>
</head>
@@ -37,53 +75,26 @@
redirectWallet("about:blank");
}
function reloadWallet() {
- window.frames["wallet"].location.reload()
+ window.frames["wallet"].location.reload();
}
function openPage() {
window.frames["other"].location =
document.getElementById("page-url").value;
}
</script>
- <button value="asd" onclick="openPopup()">open popup</button>
- <button value="asd" onclick="closeWallet();openWallet()">
- restart
- </button>
- <button value="asd" onclick="reloadWallet()">
- refresh
- </button>
- <br />
- <iframe
- id="wallet-window"
- name="wallet"
- src="wallet.html"
- style="height: calc(100% - 30px)"
- width="850"
- height="90%"
- >
+ <button onclick="openPopup()">Open Popup</button>
+ <button onclick="closeWallet();openWallet()">Restart Wallet</button>
+ <button onclick="reloadWallet()">Refresh Frame</button>
+ <div style="height: 8px"></div>
+ <iframe id="wallet-window" name="wallet" src="wallet.html" width="">
</iframe>
- <!-- <input id="page-url" type="text" />
- <button onclick="openPage()">open</button> -->
- <!-- <a
- href='javascript:void(window.frames["other"].location = "http://bank.taler:5882")'
- >open local bank</a
- >
- <hr />
- <iframe
- id="other-window"
- name="other"
- src="http://bank.taler:5882"
- width="100%"
- height="325"
- >
- </iframe> -->
<div class="overlay" id="popup-overlay" onclick="closePopup()">
-
<iframe
- id="popup-window"
- name="popup"
- src="about:blank"
- width="500"
- height="325"
+ id="popup-window"
+ name="popup"
+ src="about:blank"
+ width="500"
+ height="325"
>
</iframe>
</div>
diff --git a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
index 5e781121b..37523c667 100644
--- a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
@@ -19,7 +19,11 @@ import {
TalerError,
TalerErrorCode,
} from "@gnu-taler/taler-util";
+
import type { MessageFromBackend } from "./platform/api.js";
+// FIXME: mem leak problems
+// import { encodeCrockForURI } from "@gnu-taler/web-util/browser";
+
/**
* This will modify all the pages that the user load when navigating with Web Extension enabled
@@ -64,7 +68,9 @@ function validateTalerUri(uri: string): boolean {
function convertURIToWebExtensionPath(uri: string) {
const url = new URL(
chrome.runtime.getURL(
- `static/wallet.html#/taler-uri/${encodeURIComponent(uri)}`,
+ // FIXME: mem leak problems
+ // `static/wallet.html#/taler-uri/${encodeCrockForURI(uri)}`,
+ `static/wallet.html#/taler-uri-simple/${encodeURIComponent(uri)}`,
),
);
return url.href;
@@ -79,7 +85,7 @@ const shouldNotInject =
!rootElementIsHTML;
const logger = {
- debug: (...msg: any[]) => {},
+ debug: (...msg: any[]) => { },
info: (...msg: any[]) =>
console.log(`${new Date().toISOString()} TALER`, ...msg),
error: (...msg: any[]) =>
diff --git a/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
index 8b15380f9..c6d96ba01 100644
--- a/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
@@ -14,6 +14,8 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { encodeCrockForURI } from "@gnu-taler/web-util/browser";
+
/**
* WARNING
*
@@ -22,10 +24,10 @@
*/
(() => {
const logger = {
- debug: (...msg: any[]) => { },
- info: (...msg: any[]) =>
+ debug: (..._msg: unknown[]) => { },
+ info: (...msg: unknown[]) =>
console.log(`${new Date().toISOString()} TALER`, ...msg),
- error: (...msg: any[]) =>
+ error: (...msg: unknown[]) =>
console.error(`${new Date().toISOString()} TALER`, ...msg),
};
@@ -76,13 +78,17 @@
return undefined;
}
const host = `${config.protocol}//${config.hostname}`;
- const path = `static/wallet.html#/taler-uri/${encodeURIComponent(uri)}`;
+ // FIXME: mem leak problems
+ // const path = `static/wallet.html#/taler-uri/${encodeCrockForURI(uri)}`;
+ const path = `static/wallet.html#/taler-uri-simple/${encodeURIComponent(uri)}`;
return `${host}/${path}`;
}
function anchorOnClick(ev: MouseEvent) {
if (!(ev.currentTarget instanceof Element)) {
- logger.debug(`onclick: registered in a link that is not an HTML element`);
+ logger.debug(
+ `onclick: registered in a link that is not an HTML element`,
+ );
return;
}
const hrefAttr = ev.currentTarget.attributes.getNamedItem("href");
@@ -95,7 +101,9 @@
targetAttr && targetAttr.value ? targetAttr.value : "_self";
const page = convertURIToWebExtensionPath(hrefAttr.value);
if (!page) {
- logger.debug(`onclick: could not convert "${hrefAttr.value}" into path`);
+ logger.debug(
+ `onclick: could not convert "${hrefAttr.value}" into path`,
+ );
return;
}
// we can use window.open, but maybe some browser will block it?
@@ -118,7 +126,7 @@
function checkForNewAnchors(
mutations: MutationRecord[],
- observer: MutationObserver,
+ _observer: MutationObserver,
) {
mutations.forEach((mut) => {
if (mut.type === "childList") {
@@ -137,7 +145,7 @@
* Register the anchor handler when found
*/
function registerProtocolHandler() {
- if (document.body) overrideAllAnchor(document.body)
+ if (document.body) overrideAllAnchor(document.body);
new MutationObserver(checkForNewAnchors).observe(document, {
childList: true,
subtree: true,
@@ -179,7 +187,8 @@
};
if (apiEnabled) {
- //@ts-ignore
+ // @ts-expect-error we now that `taler` doesn't exist.
+ // we are creating the property
window.taler = taler;
}
@@ -196,5 +205,4 @@
}
start();
-})()
-
+})();
diff --git a/packages/taler-wallet-webextension/src/wallet/AddExchange/index.ts b/packages/taler-wallet-webextension/src/wallet/AddExchange/index.ts
index 94b32c157..43898ecc1 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddExchange/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddExchange/index.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { OperationFailWithBody, OperationOk, TalerExchangeApi } from "@gnu-taler/taler-util";
+import { OperationAlternative, OperationFail, OperationOk, TalerExchangeApi } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
@@ -34,13 +34,6 @@ export type State = State.Loading
| State.Confirm
| State.Verify;
-export type CheckExchangeErrors = {
- "invalid-version": string;
- "invalid-currency": string;
- "not-found": void;
- "already-active": void;
- "invalid-protocol": void;
-}
export namespace State {
export interface Loading {
@@ -73,11 +66,16 @@ export namespace State {
url: TextFieldHandler,
loading: boolean;
knownExchanges: URL[],
- result: OperationOk<TalerExchangeApi.ExchangeKeysResponse> | OperationFailWithBody<CheckExchangeErrors> | undefined,
+ result: OperationOk<TalerExchangeApi.ExchangeKeysResponse>
+ | OperationAlternative<"invalid-version", string>
+ | OperationAlternative<"invalid-currency", string>
+ | OperationFail<"not-found">
+ | OperationFail<"already-active">
+ | OperationFail<"invalid-protocol">
+ | undefined,
expectedCurrency: string | undefined,
}
}
-
const viewMapping: StateViewMap<State> = {
loading: Loading,
error: ErrorAlertView,
diff --git a/packages/taler-wallet-webextension/src/wallet/AddExchange/state.ts b/packages/taler-wallet-webextension/src/wallet/AddExchange/state.ts
index 4a04f762a..c0756d1e2 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddExchange/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddExchange/state.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { ExchangeEntryStatus, TalerExchangeHttpClient, canonicalizeBaseUrl, opKnownFailureWithBody } from "@gnu-taler/taler-util";
+import { ExchangeEntryStatus, TalerExchangeHttpClient, canonicalizeBaseUrl, opKnownFailure, opKnownFailureWithBody } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { BrowserFetchHttpLib } from "@gnu-taler/web-util/browser";
import { useCallback, useEffect, useState } from "preact/hooks";
@@ -22,7 +22,7 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { withSafe } from "../../mui/handlers.js";
import { RecursiveState } from "../../utils/index.js";
-import { CheckExchangeErrors, Props, State } from "./index.js";
+import { Props, State } from "./index.js";
function urlFromInput(str: string): URL {
let result: URL;
@@ -59,11 +59,11 @@ export function useComponentState({ onBack, currency, noDebounce }: Props): Recu
const checkExchangeBaseUrl_memo = useCallback(async function checkExchangeBaseUrl(str: string) {
const baseUrl = urlFromInput(str)
if (baseUrl.protocol !== "http:" && baseUrl.protocol !== "https:") {
- return opKnownFailureWithBody<CheckExchangeErrors>("invalid-protocol", undefined)
+ return opKnownFailure("invalid-protocol"as const)
}
const found = used.findIndex((e) => e.exchangeBaseUrl === baseUrl.href);
if (found !== -1) {
- return opKnownFailureWithBody<CheckExchangeErrors>("already-active", undefined);
+ return opKnownFailure("already-active"as const);
}
/**
@@ -84,13 +84,13 @@ export function useComponentState({ onBack, currency, noDebounce }: Props): Recu
const api = new TalerExchangeHttpClient(baseUrl.href, new BrowserFetchHttpLib() as any);
const config = await api.getConfig()
if (config.type === "fail") {
- return opKnownFailureWithBody<CheckExchangeErrors>("not-found", undefined)
+ return opKnownFailure("not-found" as const)
}
if (!api.isCompatible(config.body.version)) {
- return opKnownFailureWithBody<CheckExchangeErrors>("invalid-version", config.body.version)
+ return opKnownFailureWithBody("invalid-version"as const, config.body.version)
}
if (currency !== undefined && currency !== config.body.currency) {
- return opKnownFailureWithBody<CheckExchangeErrors>("invalid-currency", config.body.currency)
+ return opKnownFailureWithBody("invalid-currency"as const, config.body.currency)
}
const keys = await api.getKeys()
return keys
@@ -177,7 +177,7 @@ function useDebounce<T>(
setError(er);
} else {
// @ts-expect-error cause still not in typescript
- setError(new Error('unkown error on debounce', { cause: er }))
+ setError(new Error('unknown error on debounce', { cause: er }))
}
setLoading(false);
setResult(undefined);
diff --git a/packages/taler-wallet-webextension/src/wallet/AddExchange/views.tsx b/packages/taler-wallet-webextension/src/wallet/AddExchange/views.tsx
index f6537bc68..882d95670 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddExchange/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddExchange/views.tsx
@@ -120,8 +120,9 @@ export function VerifyView({
</WarningBox>
);
}
+
default: {
- assertUnreachable(result.case);
+ assertUnreachable(result);
}
}
})()}
diff --git a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx
index dd1777fd1..62f1ffbb1 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx
@@ -27,7 +27,7 @@ export interface Props {
export function AddNewActionView({ onCancel }: Props): VNode {
const [url, setUrl] = useState("");
- const uri = parseTalerUri(url);
+ const uri = parseTalerUri(url.toLowerCase());
const { i18n } = useTranslationContext();
async function redirectToWallet(): Promise<void> {
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 893122c0f..783935143 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -22,14 +22,21 @@
import {
Amounts,
+ ScopeInfo,
TalerUri,
TalerUriAction,
TranslatedString,
+ parsePaytoUri,
+ parseScopeInfoShort,
parseTalerUri,
+ stringifyPaytoUri,
+ stringifyScopeInfoShort,
stringifyTalerUri,
} from "@gnu-taler/taler-util";
import {
TranslationProvider,
+ decodeCrockFromURI,
+ encodeCrockForURI,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { createHashHistory } from "history";
@@ -87,6 +94,8 @@ import { WalletActivity } from "../components/WalletActivity.js";
import { EnabledBySettings } from "../components/EnabledBySettings.js";
import { DevExperimentPage } from "../cta/DevExperiment/index.js";
import { ConfirmAddExchangeView } from "./AddExchange/views.js";
+import { ManageAccountPage } from "./ManageAccount/index.js";
+import { SupportedBanksForAccount } from "./SupportedBanksForAccount.js";
export function Application(): VNode {
const { i18n } = useTranslationContext();
@@ -96,7 +105,7 @@ export function Application(): VNode {
redirectTo(Pages.balanceTransaction({ tid }));
}
function redirectToURL(str: string): void {
- window.location.href = new URL(str).href
+ window.location.href = new URL(str).href;
}
return (
@@ -115,12 +124,17 @@ export function Application(): VNode {
<Route
path={Pages.qr}
component={() => (
- <WalletTemplate goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <WalletTemplate
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<QrReaderPage
onDetected={(talerActionUrl: TalerUri) => {
redirectTo(
Pages.defaultCta({
- uri: stringifyTalerUri(talerActionUrl),
+ uri: encodeCrockForURI(
+ stringifyTalerUri(talerActionUrl),
+ ),
}),
);
}}
@@ -132,7 +146,10 @@ export function Application(): VNode {
<Route
path={Pages.settings}
component={() => (
- <WalletTemplate goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <WalletTemplate
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<SettingsPage />
</WalletTemplate>
)}
@@ -159,17 +176,33 @@ export function Application(): VNode {
<Route
path={Pages.balanceHistory.pattern}
- component={({ currency }: { currency?: string }) => (
- <WalletTemplate path="balance" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ component={({ scope }: { scope?: string }) => (
+ <WalletTemplate
+ path="balance"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<HistoryPage
- currency={currency}
- goToWalletDeposit={(currency: string) =>
- redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+ scope={
+ !scope
+ ? undefined
+ : parseScopeInfoShort(decodeCrockFromURI(scope))
}
- goToWalletManualWithdraw={(currency?: string) =>
+ goToWalletDeposit={(scope: ScopeInfo) =>
+ redirectTo(
+ Pages.sendCash({
+ scope: encodeCrockForURI(
+ stringifyScopeInfoShort(scope),
+ ),
+ }),
+ )
+ }
+ goToWalletManualWithdraw={(scope?: ScopeInfo) =>
redirectTo(
Pages.receiveCash({
- amount: !currency ? undefined : `${currency}:0`,
+ scope: !scope
+ ? undefined
+ : encodeCrockForURI(stringifyScopeInfoShort(scope)),
}),
)
}
@@ -179,18 +212,34 @@ export function Application(): VNode {
/>
<Route
path={Pages.searchHistory.pattern}
- component={({ currency }: { currency?: string }) => (
- <WalletTemplate path="balance" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ component={({ scope }: { scope?: string }) => (
+ <WalletTemplate
+ path="balance"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<HistoryPage
- currency={currency}
+ scope={
+ !scope
+ ? undefined
+ : parseScopeInfoShort(decodeCrockFromURI(scope))
+ }
search
- goToWalletDeposit={(currency: string) =>
- redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+ goToWalletDeposit={(scope: ScopeInfo) =>
+ redirectTo(
+ Pages.sendCash({
+ scope: encodeCrockForURI(
+ stringifyScopeInfoShort(scope),
+ ),
+ }),
+ )
}
- goToWalletManualWithdraw={(currency?: string) =>
+ goToWalletManualWithdraw={(scope?: ScopeInfo) =>
redirectTo(
Pages.receiveCash({
- amount: !currency ? undefined : `${currency}:0`,
+ scope: !scope
+ ? undefined
+ : encodeCrockForURI(stringifyScopeInfoShort(scope)),
}),
)
}
@@ -200,37 +249,127 @@ export function Application(): VNode {
/>
<Route
path={Pages.sendCash.pattern}
- component={({ amount }: { amount?: string }) => (
- <WalletTemplate path="balance" goToURL={redirectToURL}>
- <DestinationSelectionPage
- type="send"
- amount={amount}
- goToWalletBankDeposit={(amount: string) =>
- redirectTo(Pages.balanceDeposit({ amount }))
- }
- goToWalletWalletSend={(amount: string) =>
- redirectTo(Pages.ctaTransferCreate({ amount }))
- }
- />
- </WalletTemplate>
- )}
+ component={({ scope }: { scope?: string }) => {
+ if (!scope) return <Redirect to={Pages.balanceHistory({})} />;
+ const s = parseScopeInfoShort(decodeCrockFromURI(scope));
+ if (!s) return <Redirect to={Pages.balanceHistory({})} />;
+
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ <DestinationSelectionPage
+ type="send"
+ scope={s}
+ goToWalletKnownBankDeposit={(s, p) =>
+ redirectTo(
+ Pages.ctaDeposit({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ account: encodeCrockForURI(stringifyPaytoUri(p)),
+ }),
+ )
+ }
+ goToWalletNewBankDeposit={(s) =>
+ redirectTo(
+ Pages.bankManange({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ }),
+ )
+ }
+ goToWalletWalletSend={(s) =>
+ redirectTo(
+ Pages.ctaTransferCreate({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ }),
+ )
+ }
+ />
+ </WalletTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.bankManange.pattern}
+ component={({ scope }: { scope?: string }) => {
+ const s = !scope
+ ? undefined
+ : parseScopeInfoShort(decodeCrockFromURI(scope));
+ if (!s) return <div>missing scope</div>;
+
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ <ManageAccountPage
+ scope={s}
+ onAccountAdded={(account) =>
+ redirectTo(
+ Pages.ctaDeposit({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ account: encodeCrockForURI(
+ stringifyPaytoUri(account),
+ ),
+ }),
+ )
+ }
+ onCancel={() => {
+ redirectTo(
+ Pages.balanceHistory({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ }),
+ );
+ }}
+ />
+ </WalletTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.receiveCashForPurchase.pattern}
+ component={({ id: _purchaseId }: { id?: string }) => {
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ not yet implemented
+ </WalletTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.receiveCashForInvoice.pattern}
+ component={({ id: _invoiceId }: { id?: string }) => {
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ not yet implemented
+ </WalletTemplate>
+ );
+ }}
/>
<Route
path={Pages.receiveCash.pattern}
- component={({ amount }: { amount?: string }) => (
- <WalletTemplate path="balance" goToURL={redirectToURL}>
- <DestinationSelectionPage
- type="get"
- amount={amount}
- goToWalletManualWithdraw={(amount?: string) =>
- redirectTo(Pages.ctaWithdrawManual({ amount }))
- }
- goToWalletWalletInvoice={(amount?: string) =>
- redirectTo(Pages.ctaInvoiceCreate({ amount }))
- }
- />
- </WalletTemplate>
- )}
+ component={({ scope }: { scope?: string }) => {
+ const s = !scope
+ ? undefined
+ : parseScopeInfoShort(decodeCrockFromURI(scope));
+
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ <DestinationSelectionPage
+ type="get"
+ scope={s}
+ goToWalletManualWithdraw={(s) =>
+ redirectTo(
+ Pages.ctaWithdrawManualForScope({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ }),
+ )
+ }
+ goToWalletWalletInvoice={(s) =>
+ redirectTo(
+ Pages.ctaInvoiceCreate({
+ scope: encodeCrockForURI(stringifyScopeInfoShort(s)),
+ }),
+ )
+ }
+ />
+ </WalletTemplate>
+ );
+ }}
/>
<Route
@@ -239,8 +378,14 @@ export function Application(): VNode {
<WalletTemplate path="balance" goToURL={redirectToURL}>
<TransactionPage
tid={tid}
- goToWalletHistory={(currency?: string) =>
- redirectTo(Pages.balanceHistory({ currency }))
+ goToWalletHistory={(scope: ScopeInfo) =>
+ redirectTo(
+ Pages.balanceHistory({
+ scope: encodeCrockForURI(
+ stringifyScopeInfoShort(scope),
+ ),
+ }),
+ )
}
/>
</WalletTemplate>
@@ -249,25 +394,47 @@ export function Application(): VNode {
<Route
path={Pages.balanceDeposit.pattern}
- component={({ amount }: { amount: string }) => (
- <WalletTemplate path="balance" goToURL={redirectToURL}>
- <DepositPage
- amount={amount}
- onCancel={(currency: string) => {
- redirectTo(Pages.balanceHistory({ currency }));
- }}
- onSuccess={(currency: string) => {
- redirectTo(Pages.balanceHistory({ currency }));
- }}
- />
- </WalletTemplate>
- )}
+ component={({ scope }: { scope: string }) => {
+ const s = parseScopeInfoShort(decodeCrockFromURI(scope));
+ if (!s) {
+ return <div>missing scope</div>;
+ }
+ return (
+ <WalletTemplate path="balance" goToURL={redirectToURL}>
+ <DepositPage
+ scope={s}
+ onCancel={(scope: ScopeInfo) => {
+ redirectTo(
+ Pages.balanceHistory({
+ scope: encodeCrockForURI(
+ stringifyScopeInfoShort(scope),
+ ),
+ }),
+ );
+ }}
+ onSuccess={(scope: ScopeInfo) => {
+ redirectTo(
+ Pages.balanceHistory({
+ scope: encodeCrockForURI(
+ stringifyScopeInfoShort(scope),
+ ),
+ }),
+ );
+ }}
+ />
+ </WalletTemplate>
+ );
+ }}
/>
<Route
path={Pages.backup}
component={() => (
- <WalletTemplate path="backup" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <WalletTemplate
+ path="backup"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<BackupPage
onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
/>
@@ -283,8 +450,9 @@ export function Application(): VNode {
onPayProvider={(uri: string) =>
redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
}
- onWithdraw={(amount: string) =>
- redirectTo(Pages.receiveCash({ amount }))
+ onWithdraw={(_amount: string) =>
+ // FIXME: use receiveCashForPurchase
+ redirectTo(Pages.receiveCash({ scope: "FIXME missing" }))
}
onBack={() => redirectTo(Pages.backup)}
/>
@@ -314,7 +482,11 @@ export function Application(): VNode {
<Route
path={Pages.dev}
component={() => (
- <WalletTemplate path="dev" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <WalletTemplate
+ path="dev"
+ goToTransaction={redirectToTxInfo}
+ goToURL={redirectToURL}
+ >
<DeveloperPage />
</WalletTemplate>
)}
@@ -326,7 +498,7 @@ export function Application(): VNode {
<Route
path={Pages.defaultCta.pattern}
component={({ uri }: { uri: string }) => {
- const path = getPathnameForTalerURI(uri);
+ const path = getPathnameForTalerURI(decodeCrockFromURI(uri));
if (!path) {
return (
<CallToActionTemplate title={i18n.str`Taler URI handler`}>
@@ -343,14 +515,37 @@ export function Application(): VNode {
return <Redirect to={path} />;
}}
/>
+ {/* // FIXME: mem leak problems */}
+ <Route
+ path={Pages.defaultCtaSimple.pattern}
+ component={({ uri }: { uri: string }) => {
+ const path = getPathnameForTalerURI(decodeURIComponent(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 }))
+ talerPayUri={decodeCrockFromURI(talerUri)}
+ goToWalletManualWithdraw={(_amount?: string) =>
+ // FIXME: use receiveCashForPruchase
+ redirectTo(Pages.receiveCash({}))
}
cancel={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
@@ -365,9 +560,10 @@ export function Application(): VNode {
component={({ talerUri }: { talerUri: string }) => (
<CallToActionTemplate title={i18n.str`Digital cash payment`}>
<PaymentTemplatePage
- talerTemplateUri={decodeURIComponent(talerUri)}
- goToWalletManualWithdraw={(amount?: string) =>
- redirectTo(Pages.receiveCash({ amount }))
+ talerTemplateUri={decodeCrockFromURI(talerUri)}
+ goToWalletManualWithdraw={(_amount?: string) =>
+ // FIXME: use receiveCashForPruchase
+ redirectTo(Pages.receiveCash({}))
}
cancel={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
@@ -382,7 +578,7 @@ export function Application(): VNode {
component={({ talerUri }: { talerUri: string }) => (
<CallToActionTemplate title={i18n.str`Digital cash refund`}>
<RefundPage
- talerRefundUri={decodeURIComponent(talerUri)}
+ talerRefundUri={decodeCrockFromURI(talerUri)}
cancel={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
redirectTo(Pages.balanceTransaction({ tid }))
@@ -396,7 +592,7 @@ export function Application(): VNode {
component={({ talerUri }: { talerUri: string }) => (
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
<WithdrawPageFromURI
- talerWithdrawUri={decodeURIComponent(talerUri)}
+ talerWithdrawUri={!talerUri ? undefined : decodeCrockFromURI(talerUri)}
cancel={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
redirectTo(Pages.balanceTransaction({ tid }))
@@ -408,55 +604,95 @@ export function Application(): VNode {
<Route
path={Pages.ctaWithdrawManual.pattern}
component={({
+ // scope,
amount,
talerUri,
}: {
+ // scope: string;
amount: string;
talerUri: string;
- }) => (
- <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
- <WithdrawPageFromParams
- onAmountChanged={async (newamount) => {
- const page = `${Pages.ctaWithdrawManual({ amount: newamount })}?talerUri=${encodeURIComponent(talerUri)}`;
- redirectTo(page);
- }}
- talerExchangeWithdrawUri={talerUri}
- amount={amount}
- cancel={() => redirectTo(Pages.balance)}
- onSuccess={(tid: string) =>
- redirectTo(Pages.balanceTransaction({ tid }))
- }
- />
- </CallToActionTemplate>
- )}
+ }) => {
+ return (
+ <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
+ <WithdrawPageFromParams
+ scope={undefined}
+ talerExchangeWithdrawUri={!talerUri ? undefined : decodeCrockFromURI(talerUri)}
+ amount={Amounts.parse(amount)}
+ cancel={() => redirectTo(Pages.balance)}
+ onSuccess={(tid: string) =>
+ redirectTo(Pages.balanceTransaction({ tid }))
+ }
+ />
+ </CallToActionTemplate>
+ );
+ }}
/>
<Route
- path={Pages.ctaDeposit}
+ path={Pages.ctaWithdrawManualForScope.pattern}
component={({
+ scope,
amount,
- talerUri,
}: {
+ scope: string;
amount: string;
- talerUri: string;
- }) => (
- <CallToActionTemplate title={i18n.str`Digital cash deposit`}>
- <DepositPageCTA
- amountStr={Amounts.stringify(Amounts.parseOrThrow(amount))}
- talerDepositUri={decodeURIComponent(talerUri)}
- cancel={() => redirectTo(Pages.balance)}
- onSuccess={(tid: string) =>
- redirectTo(Pages.balanceTransaction({ tid }))
- }
- />
- </CallToActionTemplate>
- )}
+ }) => {
+ if (!scope) return <Redirect to={Pages.balanceHistory({})} />;
+ const s = parseScopeInfoShort(decodeCrockFromURI(scope));
+ if (!s) return <Redirect to={Pages.balanceHistory({})} />;
+
+ return (
+ <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
+ <WithdrawPageFromParams
+ talerExchangeWithdrawUri={undefined}
+ scope={s}
+ amount={Amounts.parse(amount)}
+ cancel={() => redirectTo(Pages.balance)}
+ onSuccess={(tid: string) =>
+ redirectTo(Pages.balanceTransaction({ tid }))
+ }
+ />
+ </CallToActionTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.ctaDeposit.pattern}
+ component={({
+ scope,
+ account,
+ }: {
+ scope: string;
+ account: string;
+ }) => {
+ const s = parseScopeInfoShort(decodeCrockFromURI(scope));
+ if (!s) {
+ return <div>missing scope</div>;
+ }
+ const p = parsePaytoUri(decodeCrockFromURI(account));
+ if (!p) {
+ return <div>missing account</div>;
+ }
+
+ return (
+ <CallToActionTemplate title={i18n.str`Digital cash deposit`}>
+ <DepositPageCTA
+ scope={s}
+ account={p}
+ cancel={() => redirectTo(Pages.balance)}
+ onSuccess={(tid: string) =>
+ redirectTo(Pages.balanceTransaction({ tid }))
+ }
+ />
+ </CallToActionTemplate>
+ );
+ }}
/>
<Route
path={Pages.ctaInvoiceCreate.pattern}
- component={({ amount }: { amount: string }) => (
+ component={({ scope }: { scope: string }) => (
<CallToActionTemplate title={i18n.str`Digital cash invoice`}>
<InvoiceCreatePage
- amount={Amounts.stringify(Amounts.parseOrThrow(amount))}
+ scope={parseScopeInfoShort(decodeCrockFromURI(scope))!}
onClose={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
redirectTo(Pages.balanceTransaction({ tid }))
@@ -467,10 +703,10 @@ export function Application(): VNode {
/>
<Route
path={Pages.ctaTransferCreate.pattern}
- component={({ amount }: { amount: string }) => (
+ component={({ scope }: { scope: string }) => (
<CallToActionTemplate title={i18n.str`Digital cash transfer`}>
<TransferCreatePage
- amount={Amounts.stringify(Amounts.parseOrThrow(amount))}
+ scope={parseScopeInfoShort(decodeCrockFromURI(scope))!}
onClose={() => redirectTo(Pages.balance)}
onSuccess={(tid: string) =>
redirectTo(Pages.balanceTransaction({ tid }))
@@ -481,41 +717,56 @@ export function Application(): VNode {
/>
<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>
- )}
+ component={({ talerUri }: { talerUri: string }) => {
+ const uri = (decodeCrockFromURI(talerUri));
+ if (!uri) {
+ return <div>missing taler uri</div>;
+ }
+
+ return (
+ <CallToActionTemplate title={i18n.str`Digital cash invoice`}>
+ <InvoicePayPage
+ talerPayPullUri={uri}
+ goToWalletManualWithdraw={(_amount?: string) =>
+ // FIXME: use receiveCashForInvoice
+ redirectTo(Pages.receiveCash({}))
+ }
+ 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>
- )}
+ component={({ talerUri }: { talerUri: string }) => {
+ const uri = (decodeCrockFromURI(talerUri));
+ if (!uri) {
+ return <div>missing taler uri</div>;
+ }
+
+ return (
+ <CallToActionTemplate title={i18n.str`Digital cash transfer`}>
+ <TransferPickupPage
+ talerPayPushUri={uri}
+ 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)}
+ talerRecoveryUri={!talerRecoveryUri ? undefined : decodeCrockFromURI(talerRecoveryUri)}
onCancel={() => redirectTo(Pages.balance)}
onSuccess={() => redirectTo(Pages.backup)}
/>
@@ -527,7 +778,7 @@ export function Application(): VNode {
component={({ talerUri }: { talerUri: string }) => (
<CallToActionTemplate title={i18n.str`Development experiment`}>
<DevExperimentPage
- talerExperimentUri={decodeURIComponent(talerUri)}
+ talerExperimentUri={!talerUri ? undefined : decodeCrockFromURI(talerUri)}
onCancel={() => redirectTo(Pages.balanceHistory({}))}
onSuccess={() => redirectTo(Pages.balanceHistory({}))}
/>
@@ -537,23 +788,63 @@ export function Application(): VNode {
<Route
path={Pages.ctaAddExchange}
component={({ talerUri }: { talerUri: string }) => {
- const tUri = parseTalerUri(decodeURIComponent(talerUri))
- const baseUrl = tUri?.type === TalerUriAction.AddExchange ? tUri.exchangeBaseUrl : undefined
+ const tUri = parseTalerUri(
+ decodeCrockFromURI(talerUri).toLowerCase(),
+ );
+ const baseUrl =
+ tUri?.type === TalerUriAction.AddExchange
+ ? tUri.exchangeBaseUrl
+ : undefined;
if (!baseUrl) {
- redirectTo(Pages.balanceHistory({}))
- return <div>
- invalid url {talerUri}
- </div>
+ redirectTo(Pages.balanceHistory({}));
+ return <div>invalid url {talerUri}</div>;
}
- return <CallToActionTemplate title={i18n.str`Add exchange`}>
- <ConfirmAddExchangeView
- url={baseUrl}
- status="confirm"
- error={undefined}
- onCancel={() => redirectTo(Pages.balanceHistory({}))}
- onConfirm={() => redirectTo(Pages.balanceHistory({}))}
- />
- </CallToActionTemplate>
+ return (
+ <CallToActionTemplate title={i18n.str`Add exchange`}>
+ <ConfirmAddExchangeView
+ url={baseUrl}
+ status="confirm"
+ error={undefined}
+ onCancel={() => redirectTo(Pages.balanceHistory({}))}
+ onConfirm={() => redirectTo(Pages.balanceHistory({}))}
+ />
+ </CallToActionTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.paytoBanks.pattern}
+ component={({ payto }: { payto: string }) => {
+ const pUri = parsePaytoUri(
+ decodeCrockFromURI(payto).toLowerCase(),
+ );
+ if (!pUri) {
+ redirectTo(Pages.balanceHistory({}));
+ return <div>invalid uri {pUri}</div>;
+ }
+ return (
+ <WalletTemplate goToURL={redirectToURL}>
+ <SupportedBanksForAccount account={pUri} />
+ </WalletTemplate>
+ );
+ }}
+ />
+ <Route
+ path={Pages.paytoQrs.pattern}
+ component={({ payto }: { payto: string }) => {
+ const pUri = parsePaytoUri(
+ decodeCrockFromURI(payto).toLowerCase(),
+ );
+ if (!pUri) {
+ redirectTo(Pages.balanceHistory({}));
+ return <div>invalid uri {pUri}</div>;
+ }
+ return (
+ <WalletTemplate goToURL={redirectToURL}>
+ {/* <AllQrsForAccount account={pUri} /> */}
+ <pre>{JSON.stringify({ title: "QRS", pUri })}</pre>
+ </WalletTemplate>
+ );
}}
/>
{/**
@@ -613,7 +904,7 @@ function CallToActionTemplate({
<WalletAction>
<LogoHeader />
<section style={{ display: "flex", justifyContent: "right", margin: 0 }}>
- <LinkPrimary href={Pages.balance}>
+ <LinkPrimary href={`#${Pages.balance}`}>
<div
style={{
height: 24,
@@ -633,7 +924,7 @@ function CallToActionTemplate({
{children}
</AlertProvider>
<section style={{ display: "flex", justifyContent: "right" }}>
- <LinkPrimary href={Pages.balance}>
+ <LinkPrimary href={`#${Pages.balance}`}>
<i18n.Translate>Return to wallet</i18n.Translate>
</LinkPrimary>
</section>
@@ -665,7 +956,8 @@ function WalletTemplate({
<WalletNavBar path={path} />
<PendingTransactions
goToTransaction={goToTransaction}
- goToURL={goToURL} />
+ goToURL={goToURL}
+ />
<WalletBox>
<AlertProvider>
<CurrentAlerts />
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index 8a3710f69..645fbf67c 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -261,9 +261,9 @@ function BackupLayout(props: TransactionLayoutProps): VNode {
<RowBorderGray>
<div style={{ color: !props.active ? "grey" : undefined }}>
<a
- href={Pages.backupProviderDetail({
- pid: encodeURIComponent(props.id),
- })}
+ href={`#${Pages.backupProviderDetail({
+ pid: props.id,
+ })}`}
>
<span>{props.title}</span>
</a>
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index daba6aba4..22ad1c1e7 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson, PaytoUri } from "@gnu-taler/taler-util";
+import { AmountJson, PaytoUri, ScopeInfo } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
@@ -34,9 +34,9 @@ import {
} from "./views.js";
export interface Props {
- amount?: string;
- onCancel: (currency: string) => void;
- onSuccess: (currency: string) => void;
+ scope:ScopeInfo;
+ onCancel: (scope: ScopeInfo) => void;
+ onSuccess: (scope: ScopeInfo) => void;
}
export type State =
@@ -62,8 +62,8 @@ export namespace State {
export interface AddingAccount {
status: "manage-account";
error: undefined;
- currency: string;
- onAccountAdded: (p: string) => void;
+ scope: ScopeInfo;
+ onAccountAdded: (p: PaytoUri) => void;
onCancel: () => void;
}
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index b674665cf..29f533385 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -32,47 +32,35 @@ import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";
export function useComponentState({
- amount: amountStr,
+ scope,
onCancel,
onSuccess,
}: Props): RecursiveState<State> {
const api = useBackendContext();
const { i18n } = useTranslationContext();
const { pushAlertOnError } = useAlertContext();
- const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
- const currency = parsed !== undefined ? parsed.currency : undefined;
+
+ const zero = Amounts.zeroOfCurrency(scope.currency);
const hook = useAsyncAsHook(async () => {
const { balances } = await api.wallet.call(
WalletApiOperation.GetBalances,
- {},
+ {
+ },
);
+
const { accounts } = await api.wallet.call(
WalletApiOperation.ListKnownBankAccounts,
- { currency },
+ { currency: scope.currency },
);
return { accounts, balances };
});
- const initialValue =
- parsed !== undefined
- ? parsed
- : currency !== undefined
- ? Amounts.zeroOfCurrency(currency)
- : undefined;
- // const [accountIdx, setAccountIdx] = useState<number>(0);
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
const [addingAccount, setAddingAccount] = useState(false);
- if (!currency) {
- return {
- status: "amount-or-currency-error",
- error: undefined,
- };
- }
-
if (!hook) {
return {
status: "loading",
@@ -102,9 +90,9 @@ export function useComponentState({
return {
status: "manage-account",
error: undefined,
- currency,
- onAccountAdded: (p: string) => {
- updateAccountFromList(p);
+ scope,
+ onAccountAdded: (p: PaytoUri) => {
+ updateAccountFromList(stringifyPaytoUri(p));
setAddingAccount(false);
hook.retry();
},
@@ -115,17 +103,17 @@ export function useComponentState({
};
}
- const bs = balances.filter((b) => b.available.startsWith(currency));
+ const bs = balances.filter((b) => b.scopeInfo === scope);
const balance =
bs.length > 0
? Amounts.parseOrThrow(bs[0].available)
- : Amounts.zeroOfCurrency(currency);
+ : Amounts.zeroOfCurrency(scope.currency);
if (Amounts.isZero(balance)) {
return {
status: "no-enough-balance",
error: undefined,
- currency,
+ currency: scope.currency,
};
}
@@ -133,7 +121,7 @@ export function useComponentState({
return {
status: "no-accounts",
error: undefined,
- currency,
+ currency: scope.currency,
onAddAccount: {
onClick: pushAlertOnError(async () => {
setAddingAccount(true);
@@ -143,10 +131,9 @@ export function useComponentState({
}
const firstAccount = accounts[0].uri;
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
- const zero = Amounts.zeroOfCurrency(currency)
return (): State => {
const [instructed, setInstructed] = useState(
- {amount: initialValue ?? zero, type: TransactionAmountMode.Raw},
+ { amount: zero, type: TransactionAmountMode.Raw },
);
const amountStr = Amounts.stringify(instructed.amount);
const depositPaytoUri = stringifyPaytoUri(currentAccount);
@@ -188,12 +175,12 @@ export function useComponentState({
const totalFee =
fee !== undefined
? Amounts.sub(fee.effectiveAmount, fee.rawAmount).amount
- : Amounts.zeroOfCurrency(currency);
+ : zero;
const totalToDeposit = Amounts.parseOrThrow(fee.rawAmount);
const totalEffective = Amounts.parseOrThrow(fee.effectiveAmount);
- const isDirty = instructed.amount !== initialValue;
+ const isDirty = instructed.amount !== zero;
const amountError = !isDirty
? undefined
: Amounts.cmp(balance, totalEffective) === -1
@@ -206,7 +193,7 @@ export function useComponentState({
amountError !== undefined; //amount field may be invalid
async function doSend(): Promise<void> {
- if (!currency) return;
+ // if (!currency) return;
const depositPaytoUri = stringifyPaytoUri(currentAccount);
const amountStr = Amounts.stringify(totalEffective);
@@ -214,13 +201,13 @@ export function useComponentState({
amount: amountStr,
depositPaytoUri,
});
- onSuccess(currency);
+ onSuccess(scope!);
}
return {
status: "ready",
error: undefined,
- currency,
+ currency: scope.currency,
amount: {
value: totalEffective,
onInput: pushAlertOnError(async (a) => setInstructed({
@@ -250,7 +237,7 @@ export function useComponentState({
currentAccount,
cancelHandler: {
onClick: pushAlertOnError(async () => {
- onCancel(currency);
+ onCancel(scope!);
}),
},
depositHandler: {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 1144095e1..a96f09553 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -24,6 +24,7 @@ import {
Amounts,
AmountString,
parsePaytoUri,
+ ScopeInfo,
ScopeType,
stringifyPaytoUri
} from "@gnu-taler/taler-util";
@@ -36,21 +37,26 @@ import { createWalletApiMock } from "../../test-utils.js";
import { useComponentState } from "./state.js";
const currency = "EUR";
-const amount = `${currency}:0`;
+const amount = Amounts.parseOrThrow(`${currency}:0`);
const withoutFee = (value: number): AmountResponse => ({
effectiveAmount: `${currency}:${value}` as AmountString,
rawAmount: `${currency}:${value}` as AmountString,
});
+const defaultScope: ScopeInfo = {
+ type: ScopeType.Global,
+ currency
+}
+
+
const withSomeFee = (value: number, fee: number): AmountResponse => ({
effectiveAmount: `${currency}:${value}` as AmountString,
rawAmount: `${currency}:${value - fee}` as AmountString,
});
-
describe("DepositPage states", () => {
it("should have status 'no-enough-balance' when balance is empty", async () => {
const { handler, TestingContext } = createWalletApiMock();
- const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+ const props = { scope: defaultScope, amount, onCancel: nullFunction, onSuccess: nullFunction };
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
balances: [
@@ -61,11 +67,7 @@ describe("DepositPage states", () => {
pendingIncoming: `${currency}:0` as AmountString,
pendingOutgoing: `${currency}:0` as AmountString,
requiresUserInput: false,
- scopeInfo: {
- currency,
- type: ScopeType.Auditor,
- url: "asd",
- },
+ scopeInfo: defaultScope,
},
],
});
@@ -97,7 +99,7 @@ describe("DepositPage states", () => {
it("should have status 'no-accounts' when balance is not empty and accounts is empty", async () => {
const { handler, TestingContext } = createWalletApiMock();
- const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+ const props = { scope: defaultScope, onCancel: nullFunction, onSuccess: nullFunction };
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
balances: [
@@ -108,11 +110,7 @@ describe("DepositPage states", () => {
pendingIncoming: `${currency}:0` as AmountString,
pendingOutgoing: `${currency}:0` as AmountString,
requiresUserInput: false,
- scopeInfo: {
- currency,
- type: ScopeType.Auditor,
- url: "asd",
- },
+ scopeInfo: defaultScope,
},
],
});
@@ -157,7 +155,7 @@ describe("DepositPage states", () => {
it("should have status 'ready' but unable to deposit ", async () => {
const { handler, TestingContext } = createWalletApiMock();
- const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+ const props = { scope: defaultScope, onCancel: nullFunction, onSuccess: nullFunction };
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
balances: [
@@ -168,11 +166,7 @@ describe("DepositPage states", () => {
pendingIncoming: `${currency}:0` as AmountString,
pendingOutgoing: `${currency}:0` as AmountString,
requiresUserInput: false,
- scopeInfo: {
- currency,
- type: ScopeType.Auditor,
- url: "asd",
- },
+ scopeInfo: defaultScope,
},
],
});
@@ -217,7 +211,7 @@ describe("DepositPage states", () => {
it("should not be able to deposit more than the balance ", async () => {
const { handler, TestingContext } = createWalletApiMock();
- const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+ const props = { scope: defaultScope, onCancel: nullFunction, onSuccess: nullFunction };
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
balances: [
@@ -228,11 +222,7 @@ describe("DepositPage states", () => {
pendingIncoming: `${currency}:0` as AmountString,
pendingOutgoing: `${currency}:0` as AmountString,
requiresUserInput: false,
- scopeInfo: {
- currency,
- type: ScopeType.Auditor,
- url: "asd",
- },
+ scopeInfo: defaultScope,
},
],
});
@@ -307,7 +297,7 @@ describe("DepositPage states", () => {
it("should calculate the fee upon entering amount ", async () => {
const { handler, TestingContext } = createWalletApiMock();
- const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+ const props = { scope: defaultScope, onCancel: nullFunction, onSuccess: nullFunction };
handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
balances: [
@@ -318,11 +308,7 @@ describe("DepositPage states", () => {
pendingIncoming: `${currency}:0` as AmountString,
pendingOutgoing: `${currency}:0` as AmountString,
requiresUserInput: false,
- scopeInfo: {
- currency,
- type: ScopeType.Auditor,
- url: "asd",
- },
+ scopeInfo: defaultScope,
},
],
});
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
index b56fe5523..eeb972c08 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
@@ -14,15 +14,15 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { AmountJson, KnownBankAccountsInfo, PaytoUri, ScopeInfo } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
import {
AmountFieldHandler,
- ButtonHandler,
- ToggleHandler,
+ ButtonHandler
} from "../../mui/handlers.js";
-import { compose, StateViewMap } from "../../utils/index.js";
+import { StateViewMap, compose } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView, SelectCurrencyView } from "./views.js";
@@ -30,15 +30,16 @@ export type Props = PropsGet | PropsSend;
interface PropsGet {
type: "get";
- amount?: string;
- goToWalletManualWithdraw: (amount: string) => void;
- goToWalletWalletInvoice: (amount: string) => void;
+ scope?: ScopeInfo;
+ goToWalletManualWithdraw: (s:ScopeInfo) => void;
+ goToWalletWalletInvoice: (s:ScopeInfo) => void;
}
interface PropsSend {
type: "send";
- amount?: string;
- goToWalletBankDeposit: (amount: string) => void;
- goToWalletWalletSend: (amount: string) => void;
+ scope: ScopeInfo;
+ goToWalletKnownBankDeposit: (s:ScopeInfo, p: PaytoUri) => void;
+ goToWalletNewBankDeposit: (s:ScopeInfo) => void;
+ goToWalletWalletSend: (s:ScopeInfo) => void;
}
export type State =
@@ -69,20 +70,13 @@ export namespace State {
status: "ready";
error: undefined;
type: Props["type"];
- selectCurrency: ButtonHandler;
- selectMax: ButtonHandler;
- previous: Contact[];
+ onSelectAccount: (p:PaytoUri) => void;
+ previous: KnownBankAccountsInfo[];
goToBank: ButtonHandler;
goToWallet: ButtonHandler;
- amountHandler: AmountFieldHandler;
}
}
-export type Contact = {
- icon_type: string;
- name: string;
- description: string;
-};
const viewMapping: StateViewMap<State> = {
loading: Loading,
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
index d4e270a6c..de2d439b6 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -14,7 +14,14 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts } from "@gnu-taler/taler-util";
+import {
+ ExchangeUpdateStatus,
+ KnownBankAccountsInfo,
+ PaytoUri,
+ ScopeType,
+ parseScopeInfoShort,
+ stringifyScopeInfoShort
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useState } from "preact/hooks";
@@ -22,61 +29,46 @@ import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { RecursiveState, assertUnreachable } from "../../utils/index.js";
-import { Contact, Props, State } from "./index.js";
+import { Props, State } from "./index.js";
export function useComponentState(props: Props): RecursiveState<State> {
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
+ const { i18n } = useTranslationContext();
- const parsedInitialAmount = !props.amount
- ? undefined
- : Amounts.parse(props.amount);
+ const [scope, setScope] = useState(props.scope);
const hook = useAsyncAsHook(async () => {
- if (!parsedInitialAmount) return undefined;
- const balance = await api.wallet.call(WalletApiOperation.GetBalanceDetail, {
- currency: parsedInitialAmount.currency,
- });
- return { balance };
+ const resp = await api.wallet.call(
+ WalletApiOperation.ListKnownBankAccounts,
+ {},
+ );
+ return resp
});
- const info = hook && !hook.hasError ? hook.response : undefined;
+ const previous: KnownBankAccountsInfo[] = props.type === "send" && hook && !hook.hasError ? hook.response.accounts : [];
- // const initialCurrency = parsedInitialAmount?.currency;
-
- const [amount, setAmount] = useState(
- !parsedInitialAmount ? undefined : parsedInitialAmount,
- );
- //FIXME: get this information from wallet
- // eslint-disable-next-line no-constant-condition
- const previous: Contact[] = true
- ? []
- : [
- {
- name: "International Bank",
- icon_type: "bank",
- description: "account ending with 3454",
- },
- {
- name: "Max",
- icon_type: "bank",
- description: "account ending with 3454",
- },
- {
- name: "Alex",
- icon_type: "bank",
- description: "account ending with 3454",
- },
- ];
-
- if (!amount) {
+ if (!scope) {
return () => {
- // eslint-disable-next-line react-hooks/rules-of-hooks
const { i18n } = useTranslationContext();
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const hook = useAsyncAsHook(() =>
- api.wallet.call(WalletApiOperation.ListExchanges, {}),
- );
+ const hook = useAsyncAsHook(async () => {
+ const resp = await api.wallet.call(
+ WalletApiOperation.ListExchanges,
+ {},
+ );
+
+ const unknownIndex = resp.exchanges.findIndex(
+ (d) => d.exchangeUpdateStatus === ExchangeUpdateStatus.Initial,
+ );
+ if (unknownIndex === -1) return resp;
+
+ await api.wallet.call(WalletApiOperation.UpdateExchangeEntry, {
+ exchangeBaseUrl: resp.exchanges[unknownIndex].exchangeBaseUrl,
+ force: true,
+ });
+
+ return await api.wallet.call(WalletApiOperation.ListExchanges, {});
+ });
if (!hook) {
return {
@@ -87,14 +79,30 @@ export function useComponentState(props: Props): RecursiveState<State> {
if (hook.hasError) {
return {
status: "error",
- error: alertFromError(i18n,
- i18n.str`Could not load exchanges`, hook),
+ error: alertFromError(i18n, i18n.str`Could not load exchanges`, hook),
};
}
const currencies: Record<string, string> = {};
- hook.response.exchanges.forEach((e) => {
- if (e.currency) {
- currencies[e.currency] = e.currency;
+ hook.response.exchanges.forEach((b) => {
+ switch (b.scopeInfo.type) {
+ case ScopeType.Global: {
+ currencies[stringifyScopeInfoShort(b.scopeInfo)] =
+ b.scopeInfo.currency;
+ break;
+ }
+ case ScopeType.Exchange: {
+ currencies[stringifyScopeInfoShort(b.scopeInfo)] =
+ `${b.scopeInfo.currency} ${b.scopeInfo.url}`;
+ break;
+ }
+ case ScopeType.Auditor: {
+ currencies[stringifyScopeInfoShort(b.scopeInfo)] =
+ `${b.scopeInfo.currency} ${b.scopeInfo.url}`;
+ break;
+ }
+ default: {
+ assertUnreachable(b.scopeInfo);
+ }
}
});
currencies[""] = "Select a currency";
@@ -103,55 +111,32 @@ export function useComponentState(props: Props): RecursiveState<State> {
status: "select-currency",
error: undefined,
onCurrencySelected: (c: string) => {
- setAmount(Amounts.zeroOfCurrency(c));
+ const scope = parseScopeInfoShort(c);
+ setScope(scope);
},
currencies,
};
};
}
- const currencyAndAmount = Amounts.stringify(amount);
- const invalid = Amounts.isZero(amount);
-
switch (props.type) {
case "send":
return {
status: "ready",
error: undefined,
previous,
- selectCurrency: {
- onClick: pushAlertOnError(async () => {
- setAmount(undefined);
- }),
- },
+ onSelectAccount: pushAlertOnError(async (account: PaytoUri) => {
+ props.goToWalletKnownBankDeposit(scope, account);
+ }),
goToBank: {
- onClick: invalid
- ? undefined
- : pushAlertOnError(async () => {
- props.goToWalletBankDeposit(currencyAndAmount);
- }),
- },
- selectMax: {
onClick: pushAlertOnError(async () => {
- const resp = await api.wallet.call(
- WalletApiOperation.GetMaxDepositAmount,
- {
- currency: amount.currency,
- },
- );
- setAmount(Amounts.parseOrThrow(resp.effectiveAmount));
+ props.goToWalletNewBankDeposit(scope);
}),
},
goToWallet: {
- onClick: invalid
- ? undefined
- : pushAlertOnError(async () => {
- props.goToWalletWalletSend(currencyAndAmount);
- }),
- },
- amountHandler: {
- onInput: pushAlertOnError(async (s) => setAmount(s)),
- value: amount,
+ onClick: pushAlertOnError(async () => {
+ props.goToWalletWalletSend(scope);
+ }),
},
type: props.type,
};
@@ -160,35 +145,16 @@ export function useComponentState(props: Props): RecursiveState<State> {
status: "ready",
error: undefined,
previous,
- selectCurrency: {
+ goToBank: {
onClick: pushAlertOnError(async () => {
- setAmount(undefined);
+ props.goToWalletManualWithdraw(scope);
}),
},
- selectMax: {
- onClick: invalid
- ? undefined
- : pushAlertOnError(async () => {
- props.goToWalletManualWithdraw(currencyAndAmount);
- }),
- },
- goToBank: {
- onClick: invalid
- ? undefined
- : pushAlertOnError(async () => {
- props.goToWalletManualWithdraw(currencyAndAmount);
- }),
- },
+ onSelectAccount: () => { },
goToWallet: {
- onClick: invalid
- ? undefined
- : pushAlertOnError(async () => {
- props.goToWalletWalletInvoice(currencyAndAmount);
- }),
- },
- amountHandler: {
- onInput: pushAlertOnError(async (s) => setAmount(s)),
- value: amount,
+ onClick: pushAlertOnError(async () => {
+ props.goToWalletWalletInvoice(scope);
+ }),
},
type: props.type,
};
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
index e1ac958f7..c530a7020 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
@@ -27,33 +27,15 @@ export default {
};
export const GetCash = tests.createExample(ReadyView, {
- amountHandler: {
- value: {
- currency: "EUR",
- fraction: 0,
- value: 2,
- },
- },
goToBank: {},
- selectMax: {},
goToWallet: {},
previous: [],
- selectCurrency: {},
type: "get",
});
export const SendCash = tests.createExample(ReadyView, {
- amountHandler: {
- value: {
- currency: "EUR",
- fraction: 0,
- value: 1,
- },
- },
- selectMax: {},
goToBank: {},
goToWallet: {},
previous: [],
- selectCurrency: {},
type: "send",
});
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
index 683378613..9e75f0b6f 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
@@ -25,7 +25,8 @@ import {
ExchangeListItem,
ExchangeTosStatus,
ExchangeUpdateStatus,
- ScopeType,
+ ScopeInfo,
+ ScopeType
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import * as tests from "@gnu-taler/web-util/testing";
@@ -34,14 +35,15 @@ import { nullFunction } from "../../mui/handlers.js";
import { createWalletApiMock } from "../../test-utils.js";
import { useComponentState } from "./state.js";
+const currency = "ARS";
const exchangeArs: ExchangeListItem = {
- currency: "ARS",
- exchangeBaseUrl: "http://",
+ currency,
+ exchangeBaseUrl: "http://exchange.test.taler.net",
masterPub: "123qwe123",
scopeInfo: {
- currency: "ARS",
+ currency,
type: ScopeType.Exchange,
- url: "http://",
+ url: "http://exchange.test.taler.net",
},
tosStatus: ExchangeTosStatus.Accepted,
exchangeEntryStatus: ExchangeEntryStatus.Used,
@@ -54,19 +56,20 @@ const exchangeArs: ExchangeListItem = {
};
describe("Destination selection states", () => {
- it("should select currency if no amount specified", async () => {
+ it.skip("should select currency if no amount specified", async () => {
const { handler, TestingContext } = createWalletApiMock();
- handler.addWalletCallResponse(
- WalletApiOperation.ListExchanges,
- {},
- {
- exchanges: [exchangeArs],
- },
- );
+ handler.addWalletCallResponse(WalletApiOperation.ListExchanges, undefined, {
+ exchanges: [exchangeArs],
+ });
const props = {
type: "get" as const,
+ // scope: {
+ // currency: "ARS",
+ // type: ScopeType.Exchange,
+ // url: "http://asd.com",
+ // } as ScopeInfo,
goToWalletManualWithdraw: nullFunction,
goToWalletWalletInvoice: nullFunction,
};
@@ -82,7 +85,8 @@ describe("Destination selection states", () => {
if (state.status !== "select-currency") expect.fail();
if (state.error) expect.fail();
expect(state.currencies).deep.eq({
- ARS: "ARS",
+ "ARS/http%3A%2F%2Fexchange.test.taler.net":
+ "ARS http://exchange.test.taler.net",
"": "Select a currency",
});
@@ -94,9 +98,6 @@ describe("Destination selection states", () => {
expect(state.goToBank.onClick).eq(undefined);
expect(state.goToWallet.onClick).eq(undefined);
- expect(state.amountHandler.value).deep.eq(
- Amounts.parseOrThrow("ARS:0"),
- );
},
],
TestingContext,
@@ -106,14 +107,19 @@ describe("Destination selection states", () => {
expect(handler.getCallingQueueState()).eq("empty");
});
- it("should be possible to start with an amount specified in request params", async () => {
+ it.skip("should be possible to start with an amount specified in request params", async () => {
const { handler, TestingContext } = createWalletApiMock();
const props = {
type: "get" as const,
+ scope: {
+ currency: "ARS",
+ type: ScopeType.Exchange,
+ url: "http://asd.com",
+ } as ScopeInfo,
goToWalletManualWithdraw: nullFunction,
goToWalletWalletInvoice: nullFunction,
- amount: "ARS:2",
+ amount: Amounts.parseOrThrow("ARS:2"),
};
const hookBehavior = await tests.hookBehaveLikeThis(
@@ -129,19 +135,6 @@ describe("Destination selection states", () => {
expect(state.goToBank.onClick).not.eq(undefined);
expect(state.goToWallet.onClick).not.eq(undefined);
- expect(state.amountHandler.value).deep.eq(
- Amounts.parseOrThrow("ARS:2"),
- );
- },
- (state) => {
- if (state.status !== "ready") expect.fail();
- if (state.error) expect.fail();
- expect(state.goToBank.onClick).not.eq(undefined);
- expect(state.goToWallet.onClick).not.eq(undefined);
-
- expect(state.amountHandler.value).deep.eq(
- Amounts.parseOrThrow("ARS:2"),
- );
},
],
TestingContext,
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
index 8a74a20f1..cf34ceb35 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
@@ -14,11 +14,10 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { KnownBankAccountsInfo, PaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { styled } from "@linaria/react";
import { Fragment, h, VNode } from "preact";
-import { AmountField } from "../../components/AmountField.js";
-import { EnabledBySettings } from "../../components/EnabledBySettings.js";
import { SelectList } from "../../components/SelectList.js";
import {
Input,
@@ -33,7 +32,7 @@ import { Pages } from "../../NavigationBar.js";
import arrowIcon from "../../svg/chevron-down.inline.svg";
import bankIcon from "../../svg/ri-bank-line.inline.svg";
import { assertUnreachable } from "../../utils/index.js";
-import { Contact, State } from "./index.js";
+import { State } from "./index.js";
export function SelectCurrencyView({
currencies,
@@ -62,7 +61,7 @@ export function SelectCurrencyView({
</p>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div />
- <LinkPrimary href={Pages.settingsExchangeAdd({})}>
+ <LinkPrimary href={`#${Pages.settingsExchangeAdd({})}`}>
<i18n.Translate>Add an exchange</i18n.Translate>
</LinkPrimary>
</div>
@@ -81,7 +80,6 @@ const Container = styled.div`
const ContactTable = styled.table`
width: 100%;
& > tr > td {
- padding: 8px;
& > div:not([data-disabled]):hover {
background-color: lightblue;
}
@@ -192,10 +190,8 @@ export function ReadyView(props: State.Ready): VNode {
}
}
export function ReadyGetView({
- amountHandler,
goToBank,
goToWallet,
- selectCurrency,
previous,
}: State.Ready): VNode {
const { i18n } = useTranslationContext();
@@ -203,19 +199,8 @@ export function ReadyGetView({
return (
<Container>
<h1>
- <i18n.Translate>Specify the amount and the origin</i18n.Translate>
+ <i18n.Translate>Specify the origin</i18n.Translate>
</h1>
- <Grid container columns={2} justifyContent="space-between">
- <AmountField
- label={i18n.str`Amount`}
- required
- handler={amountHandler}
- />
-
- <Button onClick={selectCurrency.onClick}>
- <i18n.Translate>Change currency</i18n.Translate>
- </Button>
- </Grid>
<Grid container spacing={1} columns={1}>
{previous.length > 0 ? (
@@ -231,7 +216,7 @@ export function ReadyGetView({
<td>
<RowExample
info={info}
- disabled={!amountHandler.onInput}
+ // disabled={!amountHandler.onInput}
/>
</td>
</tr>
@@ -241,26 +226,11 @@ export function ReadyGetView({
</Grid>
</Fragment>
) : undefined}
- {previous.length > 0 ? (
- <Grid item>
- <p>
- <i18n.Translate>
- Or specify the origin of the money
- </i18n.Translate>
- </p>
- </Grid>
- ) : (
- <Grid item>
- <p>
- <i18n.Translate>Specify the origin of the money</i18n.Translate>
- </p>
- </Grid>
- )}
<Grid item container columns={2} spacing={1}>
<Grid item xs={1}>
<Paper style={{ padding: 8 }}>
<p>
- <i18n.Translate>From my bank account</i18n.Translate>
+ <i18n.Translate>From another bank account</i18n.Translate>
</p>
<Button onClick={goToBank.onClick}>
<i18n.Translate>Withdraw</i18n.Translate>
@@ -280,7 +250,7 @@ export function ReadyGetView({
<Grid item xs={1}>
<Paper style={{ padding: 8 }}>
<p>
- <i18n.Translate>From a <pre style={{display:"inline"}}>taler://peer-push-credit</pre> URI</i18n.Translate>
+ <i18n.Translate>From a <pre style={{display:"inline"}}>taler://</pre> URI or QR code</i18n.Translate>
</p>
<a href={Pages.qr}>
<i18n.Translate>Enter URI here</i18n.Translate>
@@ -293,33 +263,19 @@ export function ReadyGetView({
);
}
export function ReadySendView({
- amountHandler,
goToBank,
+ onSelectAccount,
goToWallet,
previous,
- selectMax,
}: State.Ready): VNode {
const { i18n } = useTranslationContext();
return (
<Container>
<h1>
- <i18n.Translate>Specify the amount and the destination</i18n.Translate>
+ <i18n.Translate>Specify the destination</i18n.Translate>
</h1>
- <Grid container columns={2} justifyContent="space-between">
- <AmountField
- label={i18n.str`Amount`}
- required
- handler={amountHandler}
- />
- <EnabledBySettings name="advancedMode">
- <Button onClick={selectMax.onClick}>
- <i18n.Translate>Send all</i18n.Translate>
- </Button>
- </EnabledBySettings>
- </Grid>
-
<Grid container spacing={1} columns={1}>
{previous.length > 0 ? (
<Fragment>
@@ -334,7 +290,10 @@ export function ReadySendView({
<td>
<RowExample
info={info}
- disabled={!amountHandler.onInput}
+ onClick={() => {
+ onSelectAccount(info.uri)
+ }}
+ // disabled={!amountHandler.onInput}
/>
</td>
</tr>
@@ -344,28 +303,11 @@ export function ReadySendView({
</Grid>
</Fragment>
) : undefined}
- {previous.length > 0 ? (
- <Grid item>
- <p>
- <i18n.Translate>
- Or specify the destination of the money
- </i18n.Translate>
- </p>
- </Grid>
- ) : (
- <Grid item>
- <p>
- <i18n.Translate>
- Specify the destination of the money
- </i18n.Translate>
- </p>
- </Grid>
- )}
<Grid item container columns={2} spacing={1}>
<Grid item xs={1}>
<Paper style={{ padding: 8 }}>
<p>
- <i18n.Translate>To my bank account</i18n.Translate>
+ <i18n.Translate>To another bank account</i18n.Translate>
</p>
<Button onClick={goToBank.onClick}>
<i18n.Translate>Deposit</i18n.Translate>
@@ -391,31 +333,31 @@ export function ReadySendView({
function RowExample({
info,
disabled,
+ onClick
}: {
- info: Contact;
+ info: KnownBankAccountsInfo;
disabled?: boolean;
+ onClick?: () => void;
}): VNode {
- const icon = info.icon_type === "bank" ? bankIcon : undefined;
+
+
return (
- <MediaExample data-disabled={disabled}>
+ <MediaExample data-disabled={disabled} onClick={onClick}>
<MediaLeft>
<CircleDiv>
- {icon !== undefined ? (
<SvgIcon
- title={info.name}
+ title={info.alias}
dangerouslySetInnerHTML={{
- __html: icon,
+ __html: bankIcon,
}}
color="currentColor"
/>
- ) : (
- <span>A</span>
- )}
+
</CircleDiv>
</MediaLeft>
<MediaBody>
- <span>{info.name}</span>
- <LightText>{info.description}</LightText>
+ <span>{info.alias}</span>
+ <LightText>{describeAccount(info.uri)}</LightText>
</MediaBody>
<MediaRight>
<SvgIcon
@@ -428,3 +370,21 @@ function RowExample({
</MediaExample>
);
}
+
+
+function describeAccount(p: PaytoUri): string {
+ if (!p.isKnown) {
+ return stringifyPaytoUri(p)
+ }
+ switch (p.targetType) {
+ case "iban": {
+ return p.iban
+ }
+ case "x-taler-bank": {
+ return `${p.host}/${p.account}`
+ }
+ case "bitcoin": {
+ return `${p.address}`
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 9feb03714..9c17d3cff 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -25,7 +25,10 @@ import {
stringifyWithdrawExchange,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ encodeCrockForURI,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
@@ -337,7 +340,13 @@ export function DeveloperPage(): VNode {
return (
<tr key={idx}>
<td>
- <a href={!uri ? undefined : Pages.defaultCta({ uri })}>
+ <a
+ href={
+ !uri
+ ? undefined
+ : `#${Pages.defaultCta({ uri: encodeCrockForURI(uri) })}`
+ }
+ >
{e.scopeInfo
? `${e.scopeInfo.currency} (${
e.scopeInfo.type === ScopeType.Global
@@ -461,7 +470,7 @@ export function DeveloperPage(): VNode {
)}
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div />
- <LinkPrimary href={Pages.settingsExchangeAdd({})}>
+ <LinkPrimary href={`#${Pages.settingsExchangeAdd({})}`}>
<i18n.Translate>Add an exchange</i18n.Translate>
</LinkPrimary>
</div>
@@ -667,6 +676,12 @@ function ShowAllCoins({
);
}
+/**
+ *
+ * @param str
+ * @deprecated FIXME: use a better base64 function
+ * @returns
+ */
function toBase64(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index 482b8d698..470ad0514 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -70,6 +70,7 @@ const exampleData = {
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
confirmed: false,
exchangePaytoUris: ["payto://x-taler-bank/bank/account"],
+ reserveClosingDelay: { d_us: "forever" },
type: WithdrawalType.ManualTransfer,
reserveIsReady: false,
},
@@ -168,7 +169,12 @@ export const SomeBalanceWithNoTransactions = tests.createExample(
transactionsByDate: {
"11/11/11": [],
},
- balances: [
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
+ balances: [
{
available: "TESTKUDOS:10" as AmountString,
flags: [],
@@ -183,7 +189,7 @@ export const SomeBalanceWithNoTransactions = tests.createExample(
},
},
],
- balanceIndex: 0,
+
},
);
@@ -191,6 +197,11 @@ export const OneSimpleTransaction = tests.createExample(TestedComponent, {
transactionsByDate: {
"11/11/11": [exampleData.withdraw],
},
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
balances: [
{
flags: [],
@@ -206,7 +217,7 @@ export const OneSimpleTransaction = tests.createExample(TestedComponent, {
},
},
],
- balanceIndex: 0,
+
});
export const TwoTransactionsAndZeroBalance = tests.createExample(
@@ -215,7 +226,12 @@ export const TwoTransactionsAndZeroBalance = tests.createExample(
transactionsByDate: {
"11/11/11": [exampleData.withdraw, exampleData.deposit],
},
- balances: [
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
+ balances: [
{
flags: [],
available: "USD:0" as AmountString,
@@ -230,7 +246,7 @@ export const TwoTransactionsAndZeroBalance = tests.createExample(
},
},
],
- balanceIndex: 0,
+
},
);
@@ -245,6 +261,11 @@ export const OneTransactionPending = tests.createExample(TestedComponent, {
},
],
},
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
balances: [
{
flags: [],
@@ -260,7 +281,7 @@ export const OneTransactionPending = tests.createExample(TestedComponent, {
},
},
],
- balanceIndex: 0,
+
});
export const SomeTransactions = tests.createExample(TestedComponent, {
@@ -282,6 +303,11 @@ export const SomeTransactions = tests.createExample(TestedComponent, {
exampleData.deposit,
],
},
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
balances: [
{
flags: [],
@@ -297,7 +323,7 @@ export const SomeTransactions = tests.createExample(TestedComponent, {
},
},
],
- balanceIndex: 0,
+
});
export const SomeTransactionsInDifferentStates = tests.createExample(
@@ -378,7 +404,12 @@ export const SomeTransactionsInDifferentStates = tests.createExample(
exampleData.deposit,
],
},
- balances: [
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
+ balances: [
{
flags: [],
available: "USD:10" as AmountString,
@@ -393,7 +424,7 @@ export const SomeTransactionsInDifferentStates = tests.createExample(
},
},
],
- balanceIndex: 0,
+
},
);
@@ -411,7 +442,12 @@ export const SomeTransactionsWithTwoCurrencies = tests.createExample(
exampleData.deposit,
],
},
- balances: [
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
+ balances: [
{
flags: [],
available: "USD:0" as AmountString,
@@ -439,7 +475,7 @@ export const SomeTransactionsWithTwoCurrencies = tests.createExample(
},
},
],
- balanceIndex: 0,
+
},
);
@@ -447,6 +483,11 @@ export const FiveOfficialCurrencies = tests.createExample(TestedComponent, {
transactionsByDate: {
"11/11/11": [exampleData.withdraw],
},
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
balances: [
{
flags: [],
@@ -514,7 +555,7 @@ export const FiveOfficialCurrencies = tests.createExample(TestedComponent, {
},
},
],
- balanceIndex: 0,
+
});
export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
@@ -523,7 +564,12 @@ export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
transactionsByDate: {
"11/11/11": [exampleData.withdraw],
},
- balances: [
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
+ balances: [
{
flags: [],
available: "USD:881001321230000" as AmountString,
@@ -590,7 +636,7 @@ export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
},
},
],
- balanceIndex: 0,
+
},
);
@@ -603,6 +649,11 @@ export const PeerToPeer = tests.createExample(TestedComponent, {
exampleData.push_debit,
],
},
+ scope: {
+ currency: "Ásd",
+ type: ScopeType.Auditor,
+ url: "",
+ },
balances: [
{
flags: [],
@@ -618,5 +669,4 @@ export const PeerToPeer = tests.createExample(TestedComponent, {
},
},
],
- balanceIndex: 0,
});
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx
index f81e6db9f..d67293920 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -18,9 +18,11 @@ import {
AbsoluteTime,
Amounts,
NotificationType,
+ ScopeInfo,
ScopeType,
Transaction,
WalletBalance,
+ stringifyScopeInfoShort,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -49,35 +51,34 @@ import { TextField } from "../mui/TextField.js";
import { TextFieldHandler } from "../mui/handlers.js";
interface Props {
- currency?: string;
+ scope?: ScopeInfo;
search?: boolean;
- goToWalletDeposit: (currency: string) => Promise<void>;
- goToWalletManualWithdraw: (currency?: string) => Promise<void>;
+ goToWalletDeposit: (scope: ScopeInfo) => Promise<void>;
+ goToWalletManualWithdraw: (scope?: ScopeInfo) => Promise<void>;
}
export function HistoryPage({
- currency: _c,
+ scope,
search: showSearch,
goToWalletManualWithdraw,
goToWalletDeposit,
}: Props): VNode {
const { i18n } = useTranslationContext();
const api = useBackendContext();
- const [balanceIndex, setBalanceIndex] = useState<number>(0);
+ const [selectedScope, setSelectedScope] = useState(scope);
const [search, setSearch] = useState<string>();
const [settings] = useSettings();
const state = useAsyncAsHook(async () => {
const b = await api.wallet.call(WalletApiOperation.GetBalances, {});
- const balance =
- b.balances.length > 0 ? b.balances[balanceIndex] : undefined;
+ const balances = b.balances;
const tx = await api.wallet.call(WalletApiOperation.GetTransactions, {
- scopeInfo: showSearch ? undefined : balance?.scopeInfo,
+ scopeInfo: showSearch ? undefined : selectedScope,
sort: "descending",
includeRefreshes: settings.showRefeshTransactions,
search,
});
- return { b, tx };
- }, [balanceIndex, search]);
+ return { balances, transactions: tx.transactions };
+ }, [selectedScope, search]);
useEffect(() => {
return api.listener.onUpdateNotification(
@@ -103,17 +104,17 @@ export function HistoryPage({
);
}
- if (!state.response.b.balances.length) {
+ if (!state.response.balances.length) {
return (
<NoBalanceHelp
goToWalletManualWithdraw={{
- onClick: pushAlertOnError(goToWalletManualWithdraw),
+ onClick: pushAlertOnError(() => goToWalletManualWithdraw(selectedScope)),
}}
/>
);
}
- const byDate = state.response.tx.transactions.reduce(
+ const txsByDate = state.response.transactions.reduce(
(rv, x) => {
const startDay =
x.timestamp.t_s === "never"
@@ -141,41 +142,48 @@ export function HistoryPage({
setSearch(d);
}),
}}
- transactionsByDate={byDate}
+ transactionsByDate={txsByDate}
/>
);
}
return (
<HistoryView
- balanceIndex={balanceIndex}
- changeBalanceIndex={(b) => setBalanceIndex(b)}
- balances={state.response.b.balances}
+ scope={selectedScope ?? state.response.balances[0].scopeInfo}
+ changeScope={(b) => setSelectedScope(b)}
+ balances={state.response.balances}
goToWalletManualWithdraw={goToWalletManualWithdraw}
goToWalletDeposit={goToWalletDeposit}
- transactionsByDate={byDate}
+ transactionsByDate={txsByDate}
/>
);
}
export function HistoryView({
balances,
- balanceIndex,
- changeBalanceIndex,
+ scope,
+ changeScope,
transactionsByDate,
goToWalletManualWithdraw,
goToWalletDeposit,
}: {
- balanceIndex: number;
- changeBalanceIndex: (s: number) => void;
- goToWalletDeposit: (currency: string) => Promise<void>;
- goToWalletManualWithdraw: (currency?: string) => Promise<void>;
+ scope: ScopeInfo;
+ changeScope: (scope: ScopeInfo) => void;
+ goToWalletDeposit: (scope: ScopeInfo) => Promise<void>;
+ goToWalletManualWithdraw: (scope: ScopeInfo) => Promise<void>;
transactionsByDate: Record<string, Transaction[]>;
balances: WalletBalance[];
}): VNode {
const { i18n } = useTranslationContext();
+ const scopeStr = stringifyScopeInfoShort(scope);
+ const balanceIndex = balances.findIndex(
+ (b) => stringifyScopeInfoShort(b.scopeInfo) === scopeStr,
+ );
const balance = balances[balanceIndex];
+ if (!balance) {
+ return <div>unknown scope</div>;
+ }
const available = balance
? Amounts.jsonifyAmount(balance.available)
@@ -200,9 +208,7 @@ export function HistoryView({
tooltip="Transfer money to the wallet"
startIcon={DownloadIcon}
variant="contained"
- onClick={() =>
- goToWalletManualWithdraw(balance.scopeInfo.currency)
- }
+ onClick={() => goToWalletManualWithdraw(balance.scopeInfo)}
>
<i18n.Translate>Receive</i18n.Translate>
</Button>
@@ -212,7 +218,7 @@ export function HistoryView({
startIcon={UploadIcon}
variant="outlined"
color="primary"
- onClick={() => goToWalletDeposit(balance.scopeInfo.currency)}
+ onClick={() => goToWalletDeposit(balance.scopeInfo)}
>
<i18n.Translate>Send</i18n.Translate>
</Button>
@@ -238,9 +244,8 @@ export function HistoryView({
}}
value={balanceIndex}
onChange={(e) => {
- changeBalanceIndex(
- Number.parseInt(e.currentTarget.value, 10),
- );
+ const bIdx = Number.parseInt(e.currentTarget.value, 10);
+ changeScope(balances[bIdx].scopeInfo);
}}
>
{balances.map((entry, index) => {
diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
index 3a00d48ce..a76d77709 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { KnownBankAccountsInfo } from "@gnu-taler/taler-util";
+import { KnownBankAccountsInfo, PaytoUri, ScopeInfo } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
@@ -28,8 +28,8 @@ import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
export interface Props {
- currency: string;
- onAccountAdded: (uri: string) => void;
+ scope: ScopeInfo;
+ onAccountAdded: (uri: PaytoUri) => void;
onCancel: () => void;
}
diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
index a7b2fe90f..72727ec64 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
@@ -26,38 +26,31 @@ import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { AccountByType, Props, State } from "./index.js";
-import { useSettings } from "../../hooks/useSettings.js";
export function useComponentState({
- currency,
+ scope,
onAccountAdded,
onCancel,
}: Props): State {
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
const { i18n } = useTranslationContext();
+
const hook = useAsyncAsHook(() =>
- api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
+ api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency: scope.currency }),
);
- const accountType: Record<string, string> = {
- iban: "IBAN",
- };
- const [settings] = useSettings();
- if (settings.extendedAccountTypes) {
- accountType["bitcoin"] = "Bitcoin";
- accountType["x-taler-bank"] = "Taler Bank";
- }
- const [payto, setPayto] = useState("");
- const [alias, setAlias] = useState("");
- const [type, setType] = useState("iban");
+ const hook2 = useAsyncAsHook(() =>
+ api.wallet.call(WalletApiOperation.GetDepositWireTypesForCurrency, { currency: scope.currency, scopeInfo: scope }),
+ );
- if (!hook) {
+ if (!hook || !hook2) {
return {
status: "loading",
error: undefined,
};
}
+
if (hook.hasError) {
return {
status: "error",
@@ -68,6 +61,44 @@ export function useComponentState({
};
}
+ if (hook2.hasError) {
+ return {
+ status: "error",
+ error: alertFromError(
+ i18n,
+ i18n.str`Could not load supported wire methods`,
+ hook2),
+ };
+ }
+
+ if (hook2.response.wireTypes.length === 0) {
+ return {
+ status: "error",
+ error: {
+ type: "error",
+ message: i18n.str`No wire methods supported for this currency`,
+ description: i18n.str``,
+ cause: new Error("something"),
+ context: {},
+ },
+ };
+ }
+
+ const [payto, setPayto] = useState("");
+ const [alias, setAlias] = useState("");
+ const [type, setType] = useState(hook2.response.wireTypes[0]);
+
+ const accountType: Record<string, string> = {};
+ hook2.response.wireTypes.forEach(t => {
+ if (t === "iban") {
+ accountType[t] = "IBAN"
+ } else if (t === "x-taler-bank") {
+ accountType[t] = "x-taler-bank"
+ } else if (t === "bitcoin") {
+ accountType[t] = "Bitcoin"
+ }
+ });
+
const uri = parsePaytoUri(payto);
const found =
hook.response.accounts.findIndex(
@@ -80,10 +111,10 @@ export function useComponentState({
const normalizedPayto = stringifyPaytoUri(uri);
await api.wallet.call(WalletApiOperation.AddKnownBankAccounts, {
alias,
- currency,
+ currency: scope.currency,
payto: normalizedPayto,
});
- onAccountAdded(payto);
+ onAccountAdded(uri);
}
const paytoUriError = found ? "that account is already present" : undefined;
@@ -112,7 +143,7 @@ export function useComponentState({
return {
status: "ready",
error: undefined,
- currency,
+ currency:scope.currency,
accountType: {
list: accountType,
value: type,
diff --git a/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx b/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
index 03a08016a..d205b2fa9 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
@@ -104,7 +104,7 @@ function NotificationItem({
return (
<NotificationLayout
timestamp={timestamp}
- href={Pages.balanceTransaction({ tid: info.transactionId })}
+ href={`#${Pages.balanceTransaction({ tid: info.transactionId })}`}
title="Withdrawal on hold"
subtitle="Know-your-customer validation is required"
iconPath={"K"}
@@ -115,7 +115,7 @@ function NotificationItem({
return (
<NotificationLayout
timestamp={timestamp}
- href={Pages.balanceTransaction({ tid: info.transactionId })}
+ href={`#${Pages.balanceTransaction({ tid: info.transactionId })}`}
title="Merchant has refund your payment"
subtitle="Accept or deny refund"
iconPath={"K"}
@@ -126,7 +126,7 @@ function NotificationItem({
return (
<NotificationLayout
timestamp={timestamp}
- href={`${Pages.ctaPay}?talerPayUri=${info.talerUri}`}
+ href={`#${Pages.ctaPay}?talerPayUri=${info.talerUri}`}
title="Backup provider is unpaid"
subtitle="Complete the payment or remove the service provider"
iconPath={"K"}
diff --git a/packages/taler-wallet-webextension/src/wallet/QrReader.tsx b/packages/taler-wallet-webextension/src/wallet/QrReader.tsx
index a01ea6967..9635cd077 100644
--- a/packages/taler-wallet-webextension/src/wallet/QrReader.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/QrReader.tsx
@@ -216,7 +216,7 @@ export function QrReaderPage({ onDetected }: Props): VNode {
function onChangeDetect(str: string) {
if (str) {
- const uri = parseTalerUri(str);
+ const uri = parseTalerUri(str.toLowerCase());
if (!uri) {
setError(
i18n.str`URI is not valid. Taler URI should start with "taler://"`,
@@ -233,7 +233,7 @@ export function QrReaderPage({ onDetected }: Props): VNode {
function onChange(str: string) {
if (str) {
- if (!parseTalerUri(str)) {
+ if (!parseTalerUri(str.toLowerCase())) {
setError(
i18n.str`URI is not valid. Taler URI should start with "taler://"`,
);
@@ -293,7 +293,7 @@ export function QrReaderPage({ onDetected }: Props): VNode {
setError(i18n.str`something unexpected happen: ${error}`);
}
}
- const uri = parseTalerUri(value);
+ const uri = parseTalerUri(value.toLowerCase());
return (
<Container>
diff --git a/packages/taler-wallet-webextension/src/wallet/SupportedBanksForAccount.tsx b/packages/taler-wallet-webextension/src/wallet/SupportedBanksForAccount.tsx
new file mode 100644
index 000000000..e2388c961
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/SupportedBanksForAccount.tsx
@@ -0,0 +1,60 @@
+/*
+ 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 { PaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h } from "preact";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
+import { alertFromError } from "../context/alert.js";
+import { useBackendContext } from "../context/backend.js";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
+
+interface Props {
+ account: PaytoUri;
+}
+
+export function SupportedBanksForAccount({ account }: Props): VNode {
+ const api = useBackendContext();
+ const { i18n } = useTranslationContext();
+ const state = useAsyncAsHook(() => {
+ return api.wallet.call(WalletApiOperation.GetBankingChoicesForPayto, {
+ paytoUri: stringifyPaytoUri(account),
+ });
+ });
+ if (!state) {
+ return <Loading />;
+ }
+
+ if (state.hasError) {
+ return (
+ <ErrorAlertView
+ error={alertFromError(
+ i18n,
+ i18n.str`Could not bank choices for account`,
+ state,
+ )}
+ />
+ );
+ }
+
+ return (
+ <div>
+ {state.response.choices.map((ch) => {
+ return <a href={ch.uri}>{ch.label}</a>;
+ })}
+ </div>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 194f0e0bb..94d7e4c61 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -41,7 +41,7 @@ import {
TransactionType,
TransactionWithdrawal,
WithdrawalDetails,
- WithdrawalType
+ WithdrawalType,
} from "@gnu-taler/taler-util";
import * as tests from "@gnu-taler/web-util/testing";
import beer from "../../static-dev/beer.png";
@@ -61,6 +61,7 @@ export default {
const commonTransaction: TransactionCommon = {
error: undefined,
amountRaw: "KUDOS:11" as AmountString,
+ scopes: [],
amountEffective: "KUDOS:9.2" as AmountString,
txState: {
major: TransactionMajorState.Done,
@@ -86,6 +87,7 @@ const exampleData = {
confirmed: false,
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
exchangePaytoUris: ["payto://x-taler-bank/bank.demo.taler.net/Exchange"],
+ reserveClosingDelay: { d_us: "forever" },
type: WithdrawalType.ManualTransfer,
},
} as TransactionWithdrawal,
@@ -281,12 +283,17 @@ export const WithdrawPendingManual = tests.createExample(
type: WithdrawalType.ManualTransfer,
exchangePaytoUris: ["payto://iban/ES8877998399652238"],
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
- exchangeCreditAccountDetails: [{
- paytoUri: "payto://IBAN/1231231231",
+ reserveClosingDelay: {
+ d_us: 111,
},
- {
- paytoUri: "payto://IBAN/2342342342",
- }],
+ exchangeCreditAccountDetails: [
+ {
+ paytoUri: "payto://IBAN/1231231231",
+ },
+ {
+ paytoUri: "payto://IBAN/2342342342",
+ },
+ ],
} as WithdrawalDetails,
txState: {
major: TransactionMajorState.Pending,
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 339ded173..036f75c63 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -20,11 +20,14 @@ import {
Amounts,
AmountString,
DenomLossEventType,
+ Duration,
MerchantInfo,
NotificationType,
OrderShortInfo,
parsePaytoUri,
PaytoUri,
+ ScopeInfo,
+ ScopeType,
stringifyPaytoUri,
TalerErrorCode,
TalerPreciseTimestamp,
@@ -41,7 +44,10 @@ import {
WithdrawalType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ encodeCrockForURI,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
import { styled } from "@linaria/react";
import { isPast } from "date-fns";
import { ComponentChildren, Fragment, h, VNode } from "preact";
@@ -78,7 +84,7 @@ import { assertUnreachable } from "../utils/index.js";
interface Props {
tid: string;
- goToWalletHistory: (currency?: string) => Promise<void>;
+ goToWalletHistory: (scope: ScopeInfo) => Promise<void>;
}
export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
@@ -116,7 +122,13 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
);
}
- const currency = Amounts.parse(state.response.amountRaw)?.currency;
+ const currency = Amounts.parse(state.response.amountEffective)!.currency;
+ const txScope = !state.response.scopes.length
+ ? {
+ type: ScopeType.Global as const,
+ currency,
+ }
+ : state.response.scopes[0];
return (
<TransactionView
@@ -125,44 +137,44 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
await api.wallet.call(WalletApiOperation.FailTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ // goToWalletHistory(txScope);
}}
onSuspend={async () => {
await api.wallet.call(WalletApiOperation.SuspendTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ // goToWalletHistory(txScope);
}}
onResume={async () => {
await api.wallet.call(WalletApiOperation.ResumeTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ // goToWalletHistory(txScope);
}}
onAbort={async () => {
await api.wallet.call(WalletApiOperation.AbortTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ // goToWalletHistory(txScope);
}}
onRetry={async () => {
await api.wallet.call(WalletApiOperation.RetryTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ // goToWalletHistory(txScope);
}}
onDelete={async () => {
await api.wallet.call(WalletApiOperation.DeleteTransaction, {
transactionId,
});
- goToWalletHistory(currency);
+ goToWalletHistory(txScope);
}}
onRefund={async (transactionId) => {
await api.wallet.call(WalletApiOperation.StartRefundQuery, {
transactionId,
});
}}
- onBack={() => goToWalletHistory(currency)}
+ onBack={() => goToWalletHistory(txScope)}
/>
);
}
@@ -243,7 +255,9 @@ function TransactionTemplate({
/>
) : undefined}
{transaction.txState.major === TransactionMajorState.Pending &&
- (transaction.txState.minor === TransactionMinorState.KycRequired ? (
+ (transaction.txState.minor === TransactionMinorState.KycRequired ||
+ transaction.txState.minor ===
+ TransactionMinorState.BalanceKycRequired ? (
<AlertView
alert={{
type: "warning",
@@ -268,14 +282,6 @@ function TransactionTemplate({
),
}}
/>
- ) : transaction.txState.minor ===
- TransactionMinorState.AmlRequired ? (
- <WarningBox>
- <i18n.Translate>
- The transaction has been blocked since the account required an
- AML check.
- </i18n.Translate>
- </WarningBox>
) : (
<WarningBox>
<div style={{ justifyContent: "center", lineHeight: "25px" }}>
@@ -452,8 +458,7 @@ export function TransactionView({
// ? transaction.withdrawalDetails.exchangeCreditAccountDetails ?? []
// : [];
const blockedByKycOrAml =
- transaction.txState.minor === TransactionMinorState.KycRequired ||
- transaction.txState.minor === TransactionMinorState.AmlRequired;
+ transaction.txState.minor === TransactionMinorState.KycRequired;
return (
<TransactionTemplate
transaction={transaction}
@@ -520,6 +525,30 @@ export function TransactionView({
/>
}
/>
+ {transaction.txState.major === TransactionMajorState.Aborted &&
+ transaction.withdrawalDetails.type === WithdrawalType.ManualTransfer ? (
+ <AlertView
+ alert={{
+ type: "info",
+ message: i18n.str`Withdrawal incomplete.`,
+ description: (
+ <i18n.Translate>
+ If you have already sent money to the service provider account
+ it will wire it back at{" "}
+ <Time
+ timestamp={AbsoluteTime.addDuration(
+ AbsoluteTime.fromPreciseTimestamp(transaction.timestamp),
+ Duration.fromTalerProtocolDuration(
+ transaction.withdrawalDetails.reserveClosingDelay,
+ ),
+ )}
+ format="dd MMMM yyyy, HH:mm"
+ />
+ </i18n.Translate>
+ ),
+ }}
+ />
+ ) : undefined}
</TransactionTemplate>
);
}
@@ -575,9 +604,9 @@ export function TransactionView({
<i18n.Translate>
{<Amount value={r.amountEffective} />}{" "}
<a
- href={Pages.balanceTransaction({
+ href={`#${Pages.balanceTransaction({
tid: r.transactionId,
- })}
+ })}`}
>
was refunded
</a>{" "}
@@ -714,9 +743,19 @@ export function TransactionView({
) : transaction.wireTransferProgress === 0 ? (
<AlertView
alert={{
- type: "warning",
- message: i18n.str`Wire transfer is not initiated.`,
- description: i18n.str` `,
+ type: "info",
+ message: i18n.str`Wire transfer still pending.`,
+ description: (
+ <i18n.Translate>
+ The service provider deadline to make the wire transfer is:{" "}
+ <Time
+ timestamp={AbsoluteTime.fromProtocolTimestamp(
+ transaction.wireTransferDeadline,
+ )}
+ format="dd MMMM yyyy, HH:mm"
+ />
+ </i18n.Translate>
+ ),
}}
/>
) : transaction.wireTransferProgress === 100 ? (
@@ -743,7 +782,17 @@ export function TransactionView({
alert={{
type: "info",
message: i18n.str`Wire transfer in progress.`,
- description: i18n.str` `,
+ description: (
+ <i18n.Translate>
+ The service provider deadline to make the wire transfer is:{" "}
+ <Time
+ timestamp={AbsoluteTime.fromProtocolTimestamp(
+ transaction.wireTransferDeadline,
+ )}
+ format="dd MMMM yyyy, HH:mm"
+ />
+ </i18n.Translate>
+ ),
}}
/>
)}
@@ -801,9 +850,9 @@ export function TransactionView({
>
{transaction.paymentInfo ? (
<a
- href={Pages.balanceTransaction({
+ href={`#${Pages.balanceTransaction({
tid: transaction.refundedTransactionId,
- })}
+ })}`}
>
{transaction.paymentInfo.summary}
</a>
@@ -1746,7 +1795,7 @@ function TrackingDepositDetails({
);
}
-function DepositDetails({ amount }: { amount: AmountWithFee }): VNode {
+export function DepositDetails({ amount }: { amount: AmountWithFee }): VNode {
const { i18n } = useTranslationContext();
return (
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index ab3c465c4..53028f906 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -483,6 +483,7 @@ async function reinitWallet(): Promise<void> {
return;
}
wallet.addNotificationListener((message) => {
+ logger.trace("Wallet -> Webex", message);
if (settings.showWalletActivity) {
addNewWalletActivityNotification(activity, message);
}
@@ -521,6 +522,7 @@ export async function wxMain(): Promise<void> {
logger.trace("listen all channels");
platform.listenToAllChannels(async (message, from) => {
//wait until wallet is initialized
+ logger.trace("Webex -> Wallet", message);
await afterWalletIsInitialized;
const result = await dispatch(message, from);
return result;
@@ -573,8 +575,6 @@ async function processWalletNotification(message: WalletNotification) {
message.type === NotificationType.TransactionStateTransition &&
(message.newTxState.minor === TransactionMinorState.KycRequired ||
message.oldTxState.minor === TransactionMinorState.KycRequired ||
- message.newTxState.minor === TransactionMinorState.AmlRequired ||
- message.oldTxState.minor === TransactionMinorState.AmlRequired ||
message.newTxState.minor === TransactionMinorState.BankConfirmTransfer ||
message.oldTxState.minor === TransactionMinorState.BankConfirmTransfer)
) {