aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-08-18 16:00:01 -0300
committerSebastian <sebasjm@gmail.com>2022-08-18 16:02:16 -0300
commit4ca38113abee2c0ca17b26aa55cf6a0ecafe49c9 (patch)
treee9a7ee180907dccc3595039da31ba5bb73b8bb2d /packages
parentd1980c39fc2cf5054cfa9d3a03d685d75c2662d9 (diff)
first iteration of exchange selection: added information in the exchangeDetails response from core
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-wallet-webextension/src/NavigationBar.tsx1
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/test.ts12
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Application.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeSelection.stories.tsx85
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ExchangeSelection.tsx282
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/index.stories.tsx2
7 files changed, 386 insertions, 2 deletions
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index 70fb7bdcb..42a365f8c 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -98,6 +98,7 @@ export const Pages = {
receiveCash: pageDefinition<{ amount?: string }>("/destination/get/:amount?"),
dev: "/dev",
+ exchanges: "/exchanges",
backup: "/backup",
backupProviderDetail: pageDefinition<{ pid: string }>(
"/backup/provider/:pid",
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index 5917be092..dd3f6c9c7 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -37,6 +37,18 @@ const exchanges: ExchangeListItem[] = [
tos: {
acceptedVersion: "",
},
+ auditors: [
+ {
+ auditor_pub: "pubpubpubpubpub",
+ auditor_url: "https://audotor.taler.net",
+ denomination_keys: [],
+ },
+ ],
+ denominations: [{} as any],
+ wireInfo: {
+ accounts: [],
+ feesForType: {},
+ },
},
];
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 6c08ecb71..1f375a82e 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -58,6 +58,7 @@ import {
DestinationSelectionSendCash,
} from "./DestinationSelection.js";
import { Amounts } from "@gnu-taler/taler-util";
+import { ExchangeSelection } from "./ExchangeSelection.js";
export function Application(): VNode {
const [globalNotification, setGlobalNotification] = useState<
@@ -141,6 +142,7 @@ export function Application(): VNode {
)
}
/>
+ <Route path={Pages.exchanges} component={ExchangeSelection} />
<Route
path={Pages.sendCash.pattern}
component={DestinationSelectionSendCash}
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.stories.tsx
new file mode 100644
index 000000000..b99c6c014
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.stories.tsx
@@ -0,0 +1,85 @@
+/*
+ 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { createExample } from "../test-utils.js";
+import { ExchangeSelectionView } from "./ExchangeSelection.js";
+
+export default {
+ title: "wallet/select exchange",
+};
+
+const exchangeList = [
+ {
+ currency: "KUDOS",
+ exchangeBaseUrl: "https://exchange.demo.taler.net",
+ paytoUris: [],
+ tos: {},
+ auditors: [
+ {
+ auditor_pub: "pubpubpubpubpub",
+ auditor_url: "https://audotor.taler.net",
+ denomination_keys: [],
+ },
+ ],
+ denominations: [
+ {
+ stampStart: TalerProtocolTimestamp.never(),
+ stampExpireWithdraw: TalerProtocolTimestamp.never(),
+ stampExpireLegal: TalerProtocolTimestamp.never(),
+ stampExpireDeposit: TalerProtocolTimestamp.never(),
+ },
+ ],
+ wireInfo: {
+ accounts: [],
+ feesForType: {},
+ },
+ },
+ {
+ currency: "ARS",
+ exchangeBaseUrl: "https://exchange.taler.ar",
+ paytoUris: [],
+ tos: {},
+ auditors: [
+ {
+ auditor_pub: "pubpubpubpubpub",
+ auditor_url: "https://audotor.taler.net",
+ denomination_keys: [],
+ },
+ ],
+ denominations: [
+ {
+ stampStart: TalerProtocolTimestamp.never(),
+ stampExpireWithdraw: TalerProtocolTimestamp.never(),
+ stampExpireLegal: TalerProtocolTimestamp.never(),
+ stampExpireDeposit: TalerProtocolTimestamp.never(),
+ } as any,
+ ],
+ wireInfo: {
+ accounts: [],
+ feesForType: {},
+ },
+ },
+];
+
+export const Listing = createExample(ExchangeSelectionView, {
+ exchanges: exchangeList,
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.tsx
new file mode 100644
index 000000000..1fa921429
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection.tsx
@@ -0,0 +1,282 @@
+/*
+ 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 {
+ AbsoluteTime,
+ ExchangeListItem,
+ TalerProtocolTimestamp,
+} from "@gnu-taler/taler-util";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../components/Loading.js";
+import { LoadingError } from "../components/LoadingError.js";
+import { SelectList } from "../components/SelectList.js";
+import { Input, LinkPrimary } from "../components/styled/index.js";
+import { Time } from "../components/Time.js";
+import { useTranslationContext } from "../context/translation.js";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
+import { Button } from "../mui/Button.js";
+import * as wxApi from "../wxApi.js";
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ & > * {
+ margin-bottom: 20px;
+ }
+`;
+
+interface Props {
+ initialValue?: number;
+ exchanges: ExchangeListItem[];
+ onSelected: (exchange: string) => void;
+}
+
+const ButtonGroup = styled.div`
+ & > button {
+ margin-left: 8px;
+ margin-right: 8px;
+ }
+`;
+
+export function ExchangeSelection(): VNode {
+ const hook = useAsyncAsHook(wxApi.listExchanges);
+ const { i18n } = useTranslationContext();
+ if (!hook) {
+ return <Loading />;
+ }
+ if (hook.hasError) {
+ return (
+ <LoadingError
+ error={hook}
+ title={<i18n.Translate>Could not load list of exchange</i18n.Translate>}
+ />
+ );
+ }
+ return (
+ <ExchangeSelectionView
+ exchanges={hook.response.exchanges}
+ onSelected={(exchange) => alert(`ok, selected: ${exchange}`)}
+ />
+ );
+}
+
+export function ExchangeSelectionView({
+ initialValue,
+ exchanges,
+ onSelected,
+}: Props): VNode {
+ const list: Record<string, string> = {};
+ exchanges.forEach((e, i) => (list[String(i)] = e.exchangeBaseUrl));
+
+ const [value, setValue] = useState(String(initialValue || 0));
+ const { i18n } = useTranslationContext();
+
+ if (!exchanges.length) {
+ return <div>no exchanges for listing, please add one</div>;
+ }
+
+ const current = exchanges[Number(value)];
+
+ const hasChange = value !== current.exchangeBaseUrl;
+
+ function nearestTimestamp(
+ first: TalerProtocolTimestamp,
+ second: TalerProtocolTimestamp,
+ ): TalerProtocolTimestamp {
+ const f = AbsoluteTime.fromTimestamp(first);
+ const s = AbsoluteTime.fromTimestamp(second);
+ const a = AbsoluteTime.min(f, s);
+ return AbsoluteTime.toTimestamp(a);
+ }
+
+ let nextFeeUpdate = TalerProtocolTimestamp.never();
+
+ nextFeeUpdate = Object.values(current.wireInfo.feesForType).reduce(
+ (prev, cur) => {
+ return cur.reduce((p, c) => nearestTimestamp(p, c.endStamp), prev);
+ },
+ nextFeeUpdate,
+ );
+
+ nextFeeUpdate = current.denominations.reduce((prev, cur) => {
+ return [
+ cur.stampExpireWithdraw,
+ cur.stampExpireLegal,
+ cur.stampExpireDeposit,
+ ].reduce(nearestTimestamp, prev);
+ }, nextFeeUpdate);
+
+ return (
+ <Container>
+ <h2>
+ <i18n.Translate>Service fee description</i18n.Translate>
+ </h2>
+
+ <section>
+ <div
+ style={{
+ display: "flex",
+ flexWrap: "wrap",
+ alignItems: "center",
+ justifyContent: "space-between",
+ }}
+ >
+ <p>
+ <Input>
+ <SelectList
+ label={<i18n.Translate>Known exchanges</i18n.Translate>}
+ list={list}
+ name="lang"
+ value={value}
+ onChange={(v) => setValue(v)}
+ />
+ </Input>
+ </p>
+ {hasChange ? (
+ <ButtonGroup>
+ <Button
+ variant="outlined"
+ onClick={async () => {
+ setValue(current.exchangeBaseUrl);
+ }}
+ >
+ Reset
+ </Button>
+ <Button
+ variant="contained"
+ onClick={async () => {
+ onSelected(value);
+ }}
+ >
+ Use this exchange
+ </Button>
+ </ButtonGroup>
+ ) : (
+ <Button
+ variant="outlined"
+ onClick={async () => {
+ null;
+ }}
+ >
+ Close
+ </Button>
+ )}
+ </div>
+ </section>
+ <section>
+ <dl>
+ <dt>Auditors</dt>
+ {current.auditors.map((a) => {
+ <dd>{a.auditor_url}</dd>;
+ })}
+ </dl>
+ <table>
+ <tr>
+ <td>currency</td>
+ <td>{current.currency}</td>
+ </tr>
+ <tr>
+ <td>next fee update</td>
+ <td>
+ {
+ <Time
+ timestamp={AbsoluteTime.fromTimestamp(nextFeeUpdate)}
+ format="dd MMMM yyyy, HH:mm"
+ />
+ }
+ </td>
+ </tr>
+ </table>
+ </section>
+ <section>
+ <table>
+ <thead>
+ <tr>
+ <td>Denomination operations</td>
+ <td>Current fee</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td colSpan={2}>deposit (i)</td>
+ </tr>
+
+ <tr>
+ <td>* 10</td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>* 5</td>
+ <td>0.05</td>
+ </tr>
+ <tr>
+ <td>* 1</td>
+ <td>0.01</td>
+ </tr>
+ </tbody>
+ </table>
+ </section>
+ <section>
+ <table>
+ <thead>
+ <tr>
+ <td>Wallet operations</td>
+ <td>Current fee</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>history(i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>kyc (i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>account (i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>purse (i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>wire SEPA (i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>closing SEPA(i) </td>
+ <td>0.1</td>
+ </tr>
+ <tr>
+ <td>wad SEPA (i) </td>
+ <td>0.1</td>
+ </tr>
+ </tbody>
+ </table>
+ </section>
+ <section>
+ <ButtonGroup>
+ <LinkPrimary>Privacy policy</LinkPrimary>
+ <LinkPrimary>Terms of service</LinkPrimary>
+ </ButtonGroup>
+ </section>
+ </Container>
+ );
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
index 6a500a48e..5c01b1132 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
@@ -57,7 +57,7 @@ export const WithOneExchange = createExample(TestedComponent, {
contentType: "text/plain",
},
paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"],
- },
+ } as any, //TODO: complete with auditors, wireInfo and denominations
],
});
@@ -87,7 +87,7 @@ export const WithExchangeInDifferentState = createExample(TestedComponent, {
contentType: "text/plain",
},
paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"],
- },
+ } as any, //TODO: complete with auditors, wireInfo and denominations
{
currency: "USD",
exchangeBaseUrl: "http://exchange3.taler",
diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
index 25537691d..11f1fb422 100644
--- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
@@ -36,6 +36,7 @@ import * as a15 from "./AddNewActionView.stories.js";
import * as a16 from "./DeveloperPage.stories.js";
import * as a17 from "./QrReader.stories.js";
import * as a18 from "./DestinationSelection.stories.js";
+import * as a19 from "./ExchangeSelection.stories.js";
export default [
a1,
@@ -55,4 +56,5 @@ export default [
a16,
a17,
a18,
+ a19,
];