aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-11-22 17:34:27 -0300
committerSebastian <sebasjm@gmail.com>2021-11-22 17:34:31 -0300
commit829a59e1a24d6a99ce7554d28acfd05f21baeaf8 (patch)
tree66ef9157905e71ebf9e252c533d1855f381902d0 /packages/taler-wallet-webextension/src/wallet
parenta35604fd562a72e4e266bf6a4255d89d3c1374a1 (diff)
downloadwallet-core-829a59e1a24d6a99ce7554d28acfd05f21baeaf8.tar.xz
add exchange feature
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BalancePage.tsx17
-rw-r--r--packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx28
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx67
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx152
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx75
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx62
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx130
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx31
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx5
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Settings.tsx5
10 files changed, 522 insertions, 50 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
index 04d79a5ea..0a8910646 100644
--- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
@@ -17,7 +17,7 @@
import { BalancesResponse, i18n } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { BalanceTable } from "../components/BalanceTable";
-import { ButtonPrimary, ErrorBox } from "../components/styled/index";
+import { ButtonPrimary, Centered, ErrorBox } from "../components/styled/index";
import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { PageLink } from "../renderHtml";
import * as wxApi from "../wxApi";
@@ -66,10 +66,17 @@ export function BalanceView({
if (balance.response.balances.length === 0) {
return (
<p>
- <i18n.Translate>
- You have no balance to show. Need some{" "}
- <Linker pageName="/welcome">help</Linker> getting started?
- </i18n.Translate>
+ <Centered style={{ marginTop: 100 }}>
+ <i18n.Translate>
+ You have no balance to show. Need some{" "}
+ <Linker pageName="/welcome">help</Linker> getting started?
+ </i18n.Translate>
+ <div>
+ <ButtonPrimary onClick={goToWalletManualWithdraw}>
+ Withdraw
+ </ButtonPrimary>
+ </div>
+ </Centered>
</p>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
index 1bceabd20..554952795 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
@@ -21,6 +21,7 @@
import { AmountJson, Amounts, i18n } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
+import { route } from "preact-router";
import { useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage";
import { SelectList } from "../components/SelectList";
@@ -32,7 +33,9 @@ import {
Input,
InputWithLabel,
LightText,
+ LinkPrimary,
} from "../components/styled";
+import { Pages } from "../NavigationBar";
export interface Props {
error: string | undefined;
@@ -87,12 +90,7 @@ export function CreateManualWithdraw({
return (
<Centered style={{ marginTop: 100 }}>
<BoldLight>No exchange configured</BoldLight>
- <ButtonSuccess
- //FIXME: add exchange feature
- onClick={() => {
- null;
- }}
- >
+ <ButtonSuccess onClick={() => route(Pages.exchange_add)}>
<i18n.Translate>Add exchange</i18n.Translate>
</ButtonSuccess>
</Centered>
@@ -108,8 +106,9 @@ export function CreateManualWithdraw({
/>
<h2>Manual Withdrawal</h2>
<LightText>
- Choose a exchange to create a reserve and then fill the reserve to
- withdraw the coins
+ Choose a exchange from where the coins will be withdrawn. The exchange
+ will send the coins to this wallet after receiving a wire transfer
+ with the correct subject.
</LightText>
<p>
<Input>
@@ -130,11 +129,14 @@ export function CreateManualWithdraw({
onChange={changeExchange}
/>
</Input>
- {/* <p style={{ display: "flex", justifyContent: "right" }}>
- <a href="" style={{ marginLeft: "auto" }}>
- Add new exchange
- </a>
- </p> */}
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <LinkPrimary
+ onClick={() => route(Pages.exchange_add)}
+ style={{ marginLeft: "auto" }}
+ >
+ <i18n.Translate>Add exchange</i18n.Translate>
+ </LinkPrimary>
+ </div>
{currency && (
<InputWithLabel invalid={!!amount && !parsedAmount}>
<label>Amount</label>
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx
new file mode 100644
index 000000000..2e034458a
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx
@@ -0,0 +1,67 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { termsXml } from "../cta/termsExample";
+import { createExample } from "../test-utils";
+import { View as TestedComponent } from "./ExchangeAddConfirm";
+
+export default {
+ title: "wallet/exchange add/confirm",
+ component: TestedComponent,
+ argTypes: {
+ onRetry: { action: "onRetry" },
+ onDelete: { action: "onDelete" },
+ onBack: { action: "onBack" },
+ },
+};
+
+export const TermsNotFound = createExample(TestedComponent, {
+ url: "https://exchange.demo.taler.net/",
+ terms: {
+ status: "notfound",
+ version: "1",
+ content: undefined,
+ },
+ onAccept: async () => undefined,
+});
+
+export const NewTerms = createExample(TestedComponent, {
+ url: "https://exchange.demo.taler.net/",
+ terms: {
+ status: "new",
+ version: "1",
+ content: undefined,
+ },
+ onAccept: async () => undefined,
+});
+
+export const TermsChanged = createExample(TestedComponent, {
+ url: "https://exchange.demo.taler.net/",
+ terms: {
+ status: "changed",
+ version: "1",
+ content: {
+ type: "xml",
+ document: new DOMParser().parseFromString(termsXml, "text/xml"),
+ },
+ },
+ onAccept: async () => undefined,
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx
new file mode 100644
index 000000000..5c7f94ecd
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx
@@ -0,0 +1,152 @@
+import { i18n } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import {
+ Button,
+ ButtonSuccess,
+ ButtonWarning,
+ WarningBox,
+} from "../components/styled/index";
+import { TermsOfServiceSection } from "../cta/TermsOfServiceSection";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { buildTermsOfServiceState, TermsState } from "../utils";
+import * as wxApi from "../wxApi";
+
+export interface Props {
+ url: string;
+ onCancel: () => void;
+ onConfirm: () => void;
+}
+
+export function ExchangeAddConfirmPage({
+ url,
+ onCancel,
+ onConfirm,
+}: Props): VNode {
+ const detailsHook = useAsyncAsHook(async () => {
+ const tos = await wxApi.getExchangeTos(url, ["text/xml"]);
+
+ const tosState = buildTermsOfServiceState(tos);
+
+ return { tos: tosState };
+ });
+
+ const termsNotFound: TermsState = {
+ status: "notfound",
+ version: "",
+ content: undefined,
+ };
+ const terms = !detailsHook
+ ? undefined
+ : detailsHook.hasError
+ ? termsNotFound
+ : detailsHook.response.tos;
+
+ // const [errorAccepting, setErrorAccepting] = useState<string | undefined>(
+ // undefined,
+ // );
+
+ const onAccept = async (): Promise<void> => {
+ if (!terms) return;
+ try {
+ await wxApi.setExchangeTosAccepted(url, terms.version);
+ } catch (e) {
+ if (e instanceof Error) {
+ // setErrorAccepting(e.message);
+ }
+ }
+ };
+ return (
+ <View
+ url={url}
+ onAccept={onAccept}
+ onCancel={onCancel}
+ onConfirm={onConfirm}
+ terms={terms}
+ />
+ );
+}
+
+export interface ViewProps {
+ url: string;
+ terms: TermsState | undefined;
+ onAccept: (b: boolean) => Promise<void>;
+ onCancel: () => void;
+ onConfirm: () => void;
+}
+
+export function View({
+ url,
+ terms,
+ onAccept: doAccept,
+ onConfirm,
+ onCancel,
+}: ViewProps): VNode {
+ const needsReview =
+ !terms || terms.status === "changed" || terms.status === "new";
+ const [reviewed, setReviewed] = useState<boolean>(false);
+
+ return (
+ <Fragment>
+ <section>
+ <h1>Review terms of service</h1>
+ <div>
+ Exchange URL:
+ <a href={url} target="_blank" rel="noreferrer">
+ {url}
+ </a>
+ </div>
+ </section>
+ {terms && terms.status === "notfound" && (
+ <section>
+ <WarningBox>
+ {i18n.str`Exchange doesn't have terms of service`}
+ </WarningBox>
+ </section>
+ )}
+
+ {terms && (
+ <TermsOfServiceSection
+ reviewed={reviewed}
+ reviewing={true}
+ terms={terms}
+ onAccept={(value) =>
+ doAccept(value).then(() => {
+ setReviewed(value);
+ })
+ }
+ />
+ )}
+
+ <footer>
+ <Button onClick={onCancel}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </Button>
+ {!terms && (
+ <Button disabled>
+ <i18n.Translate>Loading terms..</i18n.Translate>
+ </Button>
+ )}
+ {terms && (
+ <Fragment>
+ {needsReview && !reviewed && (
+ <ButtonSuccess disabled upperCased onClick={onConfirm}>
+ {i18n.str`Add exchange`}
+ </ButtonSuccess>
+ )}
+ {(terms.status === "accepted" || (needsReview && reviewed)) && (
+ <ButtonSuccess upperCased onClick={onConfirm}>
+ {i18n.str`Add exchange`}
+ </ButtonSuccess>
+ )}
+ {terms.status === "notfound" && (
+ <ButtonWarning upperCased onClick={onConfirm}>
+ {i18n.str`Add exchange anyway`}
+ </ButtonWarning>
+ )}
+ </Fragment>
+ )}
+ </footer>
+ </Fragment>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx
new file mode 100644
index 000000000..10449c101
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx
@@ -0,0 +1,75 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+ canonicalizeBaseUrl,
+ TalerConfigResponse,
+} from "@gnu-taler/taler-util";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { queryToSlashKeys } from "../utils";
+import * as wxApi from "../wxApi";
+import { ExchangeAddConfirmPage } from "./ExchangeAddConfirm";
+import { ExchangeSetUrlPage } from "./ExchangeSetUrl";
+
+interface Props {
+ currency: string;
+ onBack: () => void;
+}
+
+export function ExchangeAddPage({ onBack }: Props): VNode {
+ const [verifying, setVerifying] = useState<
+ { url: string; config: TalerConfigResponse } | undefined
+ >(undefined);
+
+ const knownExchangesResponse = useAsyncAsHook(wxApi.listExchanges);
+ const knownExchanges = !knownExchangesResponse
+ ? []
+ : knownExchangesResponse.hasError
+ ? []
+ : knownExchangesResponse.response.exchanges;
+
+ if (!verifying) {
+ return (
+ <ExchangeSetUrlPage
+ onCancel={onBack}
+ knownExchanges={knownExchanges}
+ onVerify={(url) => queryToSlashKeys(url)}
+ onConfirm={(url) =>
+ queryToSlashKeys<TalerConfigResponse>(url)
+ .then((config) => {
+ setVerifying({ url, config });
+ })
+ .catch((e) => e.message)
+ }
+ />
+ );
+ }
+ return (
+ <ExchangeAddConfirmPage
+ url={verifying.url}
+ onCancel={onBack}
+ onConfirm={async () => {
+ await wxApi.addExchange({
+ exchangeBaseUrl: canonicalizeBaseUrl(verifying.url),
+ forceUpdate: true,
+ });
+ onBack();
+ }}
+ />
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx
new file mode 100644
index 000000000..bc182cb70
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx
@@ -0,0 +1,62 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { createExample } from "../test-utils";
+import { queryToSlashKeys } from "../utils";
+import { ExchangeSetUrlPage as TestedComponent } from "./ExchangeSetUrl";
+
+export default {
+ title: "wallet/exchange add/set url",
+ component: TestedComponent,
+ argTypes: {
+ onRetry: { action: "onRetry" },
+ onDelete: { action: "onDelete" },
+ onBack: { action: "onBack" },
+ },
+};
+
+export const ExpectedUSD = createExample(TestedComponent, {
+ expectedCurrency: "USD",
+ onVerify: queryToSlashKeys,
+ knownExchanges: [],
+});
+
+export const ExpectedKUDOS = createExample(TestedComponent, {
+ expectedCurrency: "KUDOS",
+ onVerify: queryToSlashKeys,
+ knownExchanges: [],
+});
+
+export const InitialState = createExample(TestedComponent, {
+ onVerify: queryToSlashKeys,
+ knownExchanges: [],
+});
+
+export const WithDemoAsKnownExchange = createExample(TestedComponent, {
+ knownExchanges: [
+ {
+ currency: "TESTKUDOS",
+ exchangeBaseUrl: "https://exchange.demo.taler.net/",
+ paytoUris: [],
+ },
+ ],
+ onVerify: queryToSlashKeys,
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx
new file mode 100644
index 000000000..e87a8894f
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx
@@ -0,0 +1,130 @@
+import {
+ canonicalizeBaseUrl,
+ ExchangeListItem,
+ i18n,
+ TalerConfigResponse,
+} from "@gnu-taler/taler-util";
+import { Fragment, h } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { ErrorMessage } from "../components/ErrorMessage";
+import {
+ Button,
+ ButtonPrimary,
+ Input,
+ WarningBox,
+} from "../components/styled/index";
+
+export interface Props {
+ initialValue?: string;
+ expectedCurrency?: string;
+ knownExchanges: ExchangeListItem[];
+ onCancel: () => void;
+ onVerify: (s: string) => Promise<TalerConfigResponse | undefined>;
+ onConfirm: (url: string) => Promise<string | undefined>;
+ withError?: string;
+}
+
+export function ExchangeSetUrlPage({
+ initialValue,
+ knownExchanges,
+ expectedCurrency,
+ onCancel,
+ onVerify,
+ onConfirm,
+ withError,
+}: Props) {
+ const [value, setValue] = useState<string>(initialValue || "");
+ const [dirty, setDirty] = useState(false);
+ const [result, setResult] = useState<TalerConfigResponse | undefined>(
+ undefined,
+ );
+ const [error, setError] = useState<string | undefined>(withError);
+
+ useEffect(() => {
+ try {
+ const url = canonicalizeBaseUrl(value);
+
+ const found =
+ knownExchanges.findIndex((e) => e.exchangeBaseUrl === url) !== -1;
+
+ if (found) {
+ setError("This exchange is already known");
+ return;
+ }
+ onVerify(url)
+ .then((r) => {
+ setResult(r);
+ })
+ .catch(() => {
+ setResult(undefined);
+ });
+ setDirty(true);
+ } catch {
+ setResult(undefined);
+ }
+ }, [value]);
+
+ return (
+ <Fragment>
+ <section>
+ {!expectedCurrency ? (
+ <h1>Add new exchange</h1>
+ ) : (
+ <h2>Add exchange for {expectedCurrency}</h2>
+ )}
+ <ErrorMessage
+ title={error && "Unable to add this exchange"}
+ description={error}
+ />
+ <p>
+ <Input invalid={dirty && !!error}>
+ <label>URL</label>
+ <input
+ type="text"
+ placeholder="https://"
+ value={value}
+ onInput={(e) => setValue(e.currentTarget.value)}
+ />
+ </Input>
+ {result && (
+ <Fragment>
+ <Input>
+ <label>Version</label>
+ <input type="text" disabled value={result.version} />
+ </Input>
+ <Input>
+ <label>Currency</label>
+ <input type="text" disabled value={result.currency} />
+ </Input>
+ </Fragment>
+ )}
+ </p>
+ </section>
+ {result && expectedCurrency && expectedCurrency !== result.currency && (
+ <WarningBox>
+ This exchange doesn't match the expected currency{" "}
+ <b>{expectedCurrency}</b>
+ </WarningBox>
+ )}
+ <footer>
+ <Button onClick={onCancel}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </Button>
+ <ButtonPrimary
+ disabled={
+ !result ||
+ !!error ||
+ (expectedCurrency !== undefined &&
+ expectedCurrency !== result.currency)
+ }
+ onClick={() => {
+ const url = canonicalizeBaseUrl(value);
+ return onConfirm(url).then((r) => (r ? setError(r) : undefined));
+ }}
+ >
+ <i18n.Translate>Next</i18n.Translate>
+ </ButtonPrimary>
+ </footer>
+ </Fragment>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index 41852e38c..16f239674 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -31,6 +31,7 @@ import {
LightText,
SmallLightText,
} from "../components/styled/index";
+import { queryToSlashConfig } from "../utils";
import * as wxApi from "../wxApi";
interface Props {
@@ -38,45 +39,19 @@ interface Props {
onBack: () => void;
}
-function getJsonIfOk(r: Response) {
- if (r.ok) {
- return r.json();
- } else {
- if (r.status >= 400 && r.status < 500) {
- throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`);
- } else {
- throw new Error(
- `Try another server: (${r.status}) ${
- r.statusText || "internal server error"
- }`,
- );
- }
- }
-}
-
export function ProviderAddPage({ onBack }: Props): VNode {
const [verifying, setVerifying] = useState<
| { url: string; name: string; provider: BackupBackupProviderTerms }
| undefined
>(undefined);
- async function getProviderInfo(
- url: string,
- ): Promise<BackupBackupProviderTerms> {
- return fetch(new URL("config", url).href)
- .catch((e) => {
- throw new Error(`Network error`);
- })
- .then(getJsonIfOk);
- }
-
if (!verifying) {
return (
<SetUrlView
onCancel={onBack}
- onVerify={(url) => getProviderInfo(url)}
+ onVerify={(url) => queryToSlashConfig(url)}
onConfirm={(url, name) =>
- getProviderInfo(url)
+ queryToSlashConfig<BackupBackupProviderTerms>(url)
.then((provider) => {
setVerifying({ url, name, provider });
})
diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
index 075126dc8..f009c5ad0 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
@@ -3,6 +3,7 @@ import { Fragment, h, VNode } from "preact";
import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType";
import { QR } from "../components/QR";
import { ButtonDestructive, WarningBox } from "../components/styled";
+import { amountToString } from "../utils";
export interface Props {
reservePub: string;
payto: string;
@@ -29,10 +30,10 @@ export function ReserveCreated({
<h1>Exchange is ready for withdrawal!</h1>
<p>
To complete the process you need to wire{" "}
- <b>{Amounts.stringify(amount)}</b> to the exchange bank account
+ <b>{amountToString(amount)}</b> to the exchange bank account
</p>
<BankDetailsByPaytoType
- amount={Amounts.stringify(amount)}
+ amount={amountToString(amount)}
exchangeBaseUrl={exchangeBaseUrl}
payto={paytoURI}
subject={reservePub}
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index 586d7b53e..5f1cd89d3 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -17,12 +17,13 @@
import { ExchangeListItem, i18n } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox";
-import { ButtonPrimary } from "../components/styled";
+import { LinkPrimary } from "../components/styled";
import { useDevContext } from "../context/devContext";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { useBackupDeviceName } from "../hooks/useBackupDeviceName";
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
import { useLang } from "../hooks/useLang";
+import { Pages } from "../NavigationBar";
// import { strings as messages } from "../i18n/strings";
import * as wxApi from "../wxApi";
@@ -112,7 +113,7 @@ export function SettingsView({
)}
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div />
- <ButtonPrimary>Manage exchange</ButtonPrimary>
+ <LinkPrimary href={Pages.exchange_add}>Add an exchange</LinkPrimary>
</div>
<h2>