aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BackupPage.tsx13
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BalancePage.tsx110
-rw-r--r--packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx26
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.stories.tsx1
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.tsx33
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx15
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx13
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx94
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx119
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Settings.tsx73
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx44
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx226
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.tsx11
14 files changed, 393 insertions, 389 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index f0ae38e0f..0b0af25ab 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -35,7 +35,6 @@ import {
RowBorderGray,
SmallLightText,
SmallText,
- WalletBox,
} from "../components/styled";
import { useBackupStatus } from "../hooks/useBackupStatus";
import { Pages } from "../NavigationBar";
@@ -70,7 +69,7 @@ export function BackupView({
onSyncAll,
}: ViewProps): VNode {
return (
- <WalletBox>
+ <Fragment>
<section>
{providers.map((provider, idx) => (
<BackupLayout
@@ -106,7 +105,7 @@ export function BackupView({
</div>
</footer>
)}
- </WalletBox>
+ </Fragment>
);
}
@@ -155,7 +154,7 @@ function BackupLayout(props: TransactionLayoutProps): VNode {
);
}
-function ExpirationText({ until }: { until: Timestamp }) {
+function ExpirationText({ until }: { until: Timestamp }): VNode {
return (
<Fragment>
<CenteredText> Expires in </CenteredText>
@@ -167,14 +166,14 @@ function ExpirationText({ until }: { until: Timestamp }) {
);
}
-function colorByTimeToExpire(d: Timestamp) {
+function colorByTimeToExpire(d: Timestamp): string {
if (d.t_ms === "never") return "rgb(28, 184, 65)";
const months = differenceInMonths(d.t_ms, new Date());
return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)";
}
-function daysUntil(d: Timestamp) {
- if (d.t_ms === "never") return undefined;
+function daysUntil(d: Timestamp): string {
+ if (d.t_ms === "never") return "";
const duration = intervalToDuration({
start: d.t_ms,
end: new Date(),
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
index 9a2847670..04d79a5ea 100644
--- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
@@ -14,27 +14,23 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import {
- amountFractionalBase,
- Amounts,
- Balance,
- BalancesResponse,
- i18n,
-} from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
-import { ButtonPrimary, Centered, WalletBox } from "../components/styled/index";
-import { BalancesHook, useBalances } from "../hooks/useBalances";
-import { PageLink, renderAmount } from "../renderHtml";
+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 { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { PageLink } from "../renderHtml";
+import * as wxApi from "../wxApi";
export function BalancePage({
goToWalletManualWithdraw,
}: {
goToWalletManualWithdraw: () => void;
}): VNode {
- const balance = useBalances();
+ const state = useAsyncAsHook(wxApi.getBalance);
return (
<BalanceView
- balance={balance}
+ balance={state}
Linker={PageLink}
goToWalletManualWithdraw={goToWalletManualWithdraw}
/>
@@ -42,7 +38,7 @@ export function BalancePage({
}
export interface BalanceViewProps {
- balance: BalancesHook;
+ balance: HookResponse<BalancesResponse>;
Linker: typeof PageLink;
goToWalletManualWithdraw: () => void;
}
@@ -53,18 +49,18 @@ export function BalanceView({
goToWalletManualWithdraw,
}: BalanceViewProps): VNode {
if (!balance) {
- return <span />;
+ return <div>Loading...</div>;
}
if (balance.hasError) {
return (
- <div>
- <p>{i18n.str`Error: could not retrieve balance information.`}</p>
+ <Fragment>
+ <ErrorBox>{balance.message}</ErrorBox>
<p>
Click <Linker pageName="welcome">here</Linker> for help and
diagnostics.
</p>
- </div>
+ </Fragment>
);
}
if (balance.response.balances.length === 0) {
@@ -77,81 +73,17 @@ export function BalanceView({
</p>
);
}
- return (
- <ShowBalances
- wallet={balance.response}
- onWithdraw={goToWalletManualWithdraw}
- />
- );
-}
-
-function formatPending(entry: Balance): VNode {
- let incoming: VNode | undefined;
- let payment: VNode | undefined;
-
- // const available = Amounts.parseOrThrow(entry.available);
- const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming);
- // const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
-
- if (!Amounts.isZero(pendingIncoming)) {
- incoming = (
- <span>
- <i18n.Translate>
- <span style={{ color: "darkgreen" }}>
- {"+"}
- {renderAmount(entry.pendingIncoming)}
- </span>{" "}
- incoming
- </i18n.Translate>
- </span>
- );
- }
-
- const l = [incoming, payment].filter((x) => x !== undefined);
- if (l.length === 0) {
- return <span />;
- }
-
- if (l.length === 1) {
- return <span>({l})</span>;
- }
- return (
- <span>
- ({l[0]}, {l[1]})
- </span>
- );
-}
-function ShowBalances({
- wallet,
- onWithdraw,
-}: {
- wallet: BalancesResponse;
- onWithdraw: () => void;
-}): VNode {
return (
- <WalletBox>
+ <Fragment>
<section>
- <Centered>
- {wallet.balances.map((entry) => {
- const av = Amounts.parseOrThrow(entry.available);
- const v = av.value + av.fraction / amountFractionalBase;
- return (
- <p key={av.currency}>
- <span>
- <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "}
- <span>{av.currency}</span>
- </span>
- {formatPending(entry)}
- </p>
- );
- })}
- </Centered>
+ <BalanceTable balances={balance.response.balances} />
</section>
- <footer>
- <div />
- <ButtonPrimary onClick={onWithdraw}>Withdraw</ButtonPrimary>
+ <footer style={{ justifyContent: "space-around" }}>
+ <ButtonPrimary onClick={goToWalletManualWithdraw}>
+ Withdraw
+ </ButtonPrimary>
</footer>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
index 300e9cd57..e4955e376 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
@@ -34,6 +34,10 @@ const exchangeList = {
"http://exchange.tal": "EUR",
};
+export const WithoutAnyExchangeKnown = createExample(TestedComponent, {
+ exchangeList: {},
+});
+
export const InitialState = createExample(TestedComponent, {
exchangeList,
});
diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
index 140ac2d40..1bceabd20 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
@@ -19,17 +19,19 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
+import { AmountJson, Amounts, i18n } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage";
import { SelectList } from "../components/SelectList";
import {
+ BoldLight,
ButtonPrimary,
+ ButtonSuccess,
+ Centered,
Input,
InputWithLabel,
LightText,
- WalletBox,
} from "../components/styled";
export interface Props {
@@ -82,11 +84,23 @@ export function CreateManualWithdraw({
}
if (!initialExchange) {
- return <div>There is no known exchange where to withdraw, add one</div>;
+ return (
+ <Centered style={{ marginTop: 100 }}>
+ <BoldLight>No exchange configured</BoldLight>
+ <ButtonSuccess
+ //FIXME: add exchange feature
+ onClick={() => {
+ null;
+ }}
+ >
+ <i18n.Translate>Add exchange</i18n.Translate>
+ </ButtonSuccess>
+ </Centered>
+ );
}
return (
- <WalletBox>
+ <Fragment>
<section>
<ErrorMessage
title={error && "Can't create the reserve"}
@@ -145,6 +159,6 @@ export function CreateManualWithdraw({
Start withdrawal
</ButtonPrimary>
</footer>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index 9ae3ac3bd..0f471ac30 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -57,6 +57,7 @@ const exampleData = {
type: TransactionType.Withdrawal,
exchangeBaseUrl: "http://exchange.demo.taler.net",
withdrawalDetails: {
+ reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
confirmed: false,
exchangePaytoUris: ["payto://x-taler-bank/bank/account"],
type: WithdrawalType.ManualTransfer,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 6b1a21852..bc8ef734a 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -17,42 +17,37 @@
import {
AmountString,
Balance,
+ NotificationType,
Transaction,
- TransactionsResponse,
} from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { DateSeparator, WalletBox } from "../components/styled";
+import { DateSeparator } from "../components/styled";
import { Time } from "../components/Time";
import { TransactionItem } from "../components/TransactionItem";
-import { useBalances } from "../hooks/useBalances";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import * as wxApi from "../wxApi";
export function HistoryPage(): VNode {
- const [transactions, setTransactions] = useState<
- TransactionsResponse | undefined
- >(undefined);
- const balance = useBalances();
+ const balance = useAsyncAsHook(wxApi.getBalance);
const balanceWithoutError = balance?.hasError
? []
: balance?.response.balances || [];
- useEffect(() => {
- const fetchData = async (): Promise<void> => {
- const res = await wxApi.getTransactions();
- setTransactions(res);
- };
- fetchData();
- }, []);
+ const transactionQuery = useAsyncAsHook(wxApi.getTransactions, [
+ NotificationType.WithdrawGroupFinished,
+ ]);
- if (!transactions) {
+ if (!transactionQuery) {
return <div>Loading ...</div>;
}
+ if (transactionQuery.hasError) {
+ return <div>There was an error loading the transactions.</div>;
+ }
return (
<HistoryView
balances={balanceWithoutError}
- list={[...transactions.transactions].reverse()}
+ list={[...transactionQuery.response.transactions].reverse()}
/>
);
}
@@ -87,7 +82,7 @@ export function HistoryView({
const multiCurrency = balances.length > 1;
return (
- <WalletBox noPadding>
+ <Fragment>
{balances.length > 0 && (
<header>
{balances.length === 1 && (
@@ -128,6 +123,6 @@ export function HistoryView({
);
})}
</section>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
index 1af4e8d8d..88d5f1722 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
@@ -23,9 +23,9 @@ import {
AmountJson,
Amounts,
} from "@gnu-taler/taler-util";
-import { ReserveCreated } from "./ReserveCreated.js";
+import { ReserveCreated } from "./ReserveCreated";
import { route } from "preact-router";
-import { Pages } from "../NavigationBar.js";
+import { Pages } from "../NavigationBar";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
export function ManualWithdrawPage(): VNode {
@@ -39,7 +39,7 @@ export function ManualWithdrawPage(): VNode {
>(undefined);
const [error, setError] = useState<string | undefined>(undefined);
- const knownExchangesHook = useAsyncAsHook(() => wxApi.listExchanges());
+ const state = useAsyncAsHook(() => wxApi.listExchanges());
async function doCreate(
exchangeBaseUrl: string,
@@ -75,10 +75,13 @@ export function ManualWithdrawPage(): VNode {
);
}
- if (!knownExchangesHook || knownExchangesHook.hasError) {
- return <div>No Known exchanges</div>;
+ if (!state) {
+ return <div>loading...</div>;
}
- const exchangeList = knownExchangesHook.response.exchanges.reduce(
+ if (state.hasError) {
+ return <div>There was an error getting the known exchanges</div>;
+ }
+ const exchangeList = state.response.exchanges.reduce(
(p, c) => ({
...p,
[c.exchangeBaseUrl]: c.currency,
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index 1c7fdc829..41852e38c 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -20,7 +20,7 @@ import {
canonicalizeBaseUrl,
i18n,
} from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
+import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Checkbox } from "../components/Checkbox";
import { ErrorMessage } from "../components/ErrorMessage";
@@ -29,7 +29,6 @@ import {
ButtonPrimary,
Input,
LightText,
- WalletBox,
SmallLightText,
} from "../components/styled/index";
import * as wxApi from "../wxApi";
@@ -64,7 +63,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
async function getProviderInfo(
url: string,
): Promise<BackupBackupProviderTerms> {
- return fetch(`${url}config`)
+ return fetch(new URL("config", url).href)
.catch((e) => {
throw new Error(`Network error`);
})
@@ -137,7 +136,7 @@ export function SetUrlView({
}
}, [value]);
return (
- <WalletBox>
+ <Fragment>
<section>
<h1> Add backup provider</h1>
<ErrorMessage
@@ -182,7 +181,7 @@ export function SetUrlView({
<i18n.Translate>Next</i18n.Translate>
</ButtonPrimary>
</footer>
- </WalletBox>
+ </Fragment>
);
}
@@ -201,7 +200,7 @@ export function ConfirmProviderView({
const [accepted, setAccepted] = useState(false);
return (
- <WalletBox>
+ <Fragment>
<section>
<h1>Review terms of service</h1>
<div>
@@ -239,6 +238,6 @@ export function ConfirmProviderView({
<i18n.Translate>Add provider</i18n.Translate>
</ButtonPrimary>
</footer>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index 1c14c6e0a..d14429ee5 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -28,34 +28,62 @@ import {
ButtonPrimary,
PaymentStatus,
SmallLightText,
- WalletBox,
} from "../components/styled";
import { Time } from "../components/Time";
-import { useProviderStatus } from "../hooks/useProviderStatus";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import * as wxApi from "../wxApi";
interface Props {
pid: string;
onBack: () => void;
}
-export function ProviderDetailPage({ pid, onBack }: Props): VNode {
- const status = useProviderStatus(pid);
- if (!status) {
+export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
+ async function getProviderInfo(): Promise<ProviderInfo | null> {
+ //create a first list of backup info by currency
+ const status = await wxApi.getBackupInfo();
+
+ const providers = status.providers.filter(
+ (p) => p.syncProviderBaseUrl === providerURL,
+ );
+ return providers.length ? providers[0] : null;
+ }
+
+ const state = useAsyncAsHook(getProviderInfo);
+
+ if (!state) {
return (
<div>
<i18n.Translate>Loading...</i18n.Translate>
</div>
);
}
- if (!status.info) {
+ if (state.hasError) {
+ return (
+ <div>
+ <i18n.Translate>
+ There was an error loading the provider detail for "{providerURL}"
+ </i18n.Translate>
+ </div>
+ );
+ }
+
+ if (state.response === null) {
onBack();
- return <div />;
+ return (
+ <div>
+ <i18n.Translate>
+ There is not known provider with url "{providerURL}". Redirecting
+ back...
+ </i18n.Translate>
+ </div>
+ );
}
return (
<ProviderView
- info={status.info}
- onSync={status.sync}
- onDelete={() => status.remove().then(onBack)}
+ info={state.response}
+ onSync={async () => wxApi.syncOneProvider(providerURL)}
+ onDelete={async () => wxApi.syncOneProvider(providerURL).then(onBack)}
onBack={onBack}
onExtend={() => {
null;
@@ -84,7 +112,7 @@ export function ProviderView({
info.paymentStatus.type === ProviderPaymentType.Paid ||
info.paymentStatus.type === ProviderPaymentType.TermsChanged;
return (
- <WalletBox>
+ <Fragment>
<Error info={info} />
<header>
<h3>
@@ -167,35 +195,10 @@ export function ProviderView({
</ButtonDestructive>
</div>
</footer>
- </WalletBox>
+ </Fragment>
);
}
-// function daysSince(d?: Timestamp): string {
-// if (!d || d.t_ms === "never") return "never synced";
-// const duration = intervalToDuration({
-// start: d.t_ms,
-// end: new Date(),
-// });
-// const str = formatDuration(duration, {
-// delimiter: ", ",
-// format: [
-// duration?.years
-// ? i18n.str`years`
-// : duration?.months
-// ? i18n.str`months`
-// : duration?.days
-// ? i18n.str`days`
-// : duration?.hours
-// ? i18n.str`hours`
-// : duration?.minutes
-// ? i18n.str`minutes`
-// : i18n.str`seconds`,
-// ],
-// });
-// return `synced ${str} ago`;
-// }
-
function Error({ info }: { info: ProviderInfo }): VNode {
if (info.lastError) {
return <ErrorMessage title={info.lastError.hint} />;
@@ -234,23 +237,6 @@ function Error({ info }: { info: ProviderInfo }): VNode {
return <Fragment />;
}
-// function colorByStatus(status: ProviderPaymentType): string {
-// switch (status) {
-// case ProviderPaymentType.InsufficientBalance:
-// return "rgb(223, 117, 20)";
-// case ProviderPaymentType.Unpaid:
-// return "rgb(202, 60, 60)";
-// case ProviderPaymentType.Paid:
-// return "rgb(28, 184, 65)";
-// case ProviderPaymentType.Pending:
-// return "gray";
-// // case ProviderPaymentType.InsufficientBalance:
-// // return "rgb(202, 60, 60)";
-// case ProviderPaymentType.TermsChanged:
-// return "rgb(202, 60, 60)";
-// }
-// }
-
function descriptionByStatus(status: ProviderPaymentStatus): VNode {
switch (status.type) {
// return i18n.str`no enough balance to make the payment`
diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
index a72026ab8..075126dc8 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
@@ -1,18 +1,8 @@
-import {
- AmountJson,
- Amounts,
- parsePaytoUri,
- PaytoUri,
-} from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
+import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType";
import { QR } from "../components/QR";
-import {
- ButtonDestructive,
- ButtonPrimary,
- WalletBox,
- WarningBox,
-} from "../components/styled";
+import { ButtonDestructive, WarningBox } from "../components/styled";
export interface Props {
reservePub: string;
payto: string;
@@ -21,92 +11,6 @@ export interface Props {
onBack: () => void;
}
-interface BankDetailsProps {
- payto: PaytoUri;
- exchangeBaseUrl: string;
- subject: string;
- amount: string;
-}
-
-function Row({
- name,
- value,
- literal,
-}: {
- name: string;
- value: string;
- literal?: boolean;
-}): VNode {
- const [copied, setCopied] = useState(false);
- function copyText(): void {
- navigator.clipboard.writeText(value);
- setCopied(true);
- }
- useEffect(() => {
- setTimeout(() => {
- setCopied(false);
- }, 1000);
- }, [copied]);
- return (
- <tr>
- <td>
- {!copied ? (
- <ButtonPrimary small onClick={copyText}>
- &nbsp; Copy &nbsp;
- </ButtonPrimary>
- ) : (
- <ButtonPrimary small disabled>
- Copied
- </ButtonPrimary>
- )}
- </td>
- <td>
- <b>{name}</b>
- </td>
- {literal ? (
- <td>
- <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
- {value}
- </pre>
- </td>
- ) : (
- <td>{value}</td>
- )}
- </tr>
- );
-}
-
-function BankDetailsByPaytoType({
- payto,
- subject,
- exchangeBaseUrl,
- amount,
-}: BankDetailsProps): VNode {
- const firstPart = !payto.isKnown ? (
- <Fragment>
- <Row name="Account" value={payto.targetPath} />
- <Row name="Exchange" value={exchangeBaseUrl} />
- </Fragment>
- ) : payto.targetType === "x-taler-bank" ? (
- <Fragment>
- <Row name="Bank host" value={payto.host} />
- <Row name="Bank account" value={payto.account} />
- <Row name="Exchange" value={exchangeBaseUrl} />
- </Fragment>
- ) : payto.targetType === "iban" ? (
- <Fragment>
- <Row name="IBAN" value={payto.iban} />
- <Row name="Exchange" value={exchangeBaseUrl} />
- </Fragment>
- ) : undefined;
- return (
- <table>
- {firstPart}
- <Row name="Amount" value={amount} />
- <Row name="Subject" value={subject} literal />
- </table>
- );
-}
export function ReserveCreated({
reservePub,
payto,
@@ -120,11 +24,12 @@ export function ReserveCreated({
return <div>could not parse payto uri from exchange {payto}</div>;
}
return (
- <WalletBox>
+ <Fragment>
<section>
- <h1>Bank transfer details</h1>
+ <h1>Exchange is ready for withdrawal!</h1>
<p>
- Please wire <b>{Amounts.stringify(amount)}</b> to:
+ To complete the process you need to wire{" "}
+ <b>{Amounts.stringify(amount)}</b> to the exchange bank account
</p>
<BankDetailsByPaytoType
amount={Amounts.stringify(amount)}
@@ -132,14 +37,14 @@ export function ReserveCreated({
payto={paytoURI}
subject={reservePub}
/>
- </section>
- <section>
<p>
<WarningBox>
Make sure to use the correct subject, otherwise the money will not
arrive in this wallet.
</WarningBox>
</p>
+ </section>
+ <section>
<p>
Alternative, you can also scan this QR code or open{" "}
<a href={payto}>this link</a> if you have a banking app installed that
@@ -149,8 +54,10 @@ export function ReserveCreated({
</section>
<footer>
<div />
- <ButtonDestructive onClick={onBack}>Cancel withdraw</ButtonDestructive>
+ <ButtonDestructive onClick={onBack}>
+ Cancel withdrawal
+ </ButtonDestructive>
</footer>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index 8d8f3cdbc..586d7b53e 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -15,16 +15,15 @@
*/
import { ExchangeListItem, i18n } from "@gnu-taler/taler-util";
-import { VNode, h, Fragment } from "preact";
+import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox";
-import { EditableText } from "../components/EditableText";
-import { SelectList } from "../components/SelectList";
-import { ButtonPrimary, ButtonSuccess, WalletBox } from "../components/styled";
+import { ButtonPrimary } from "../components/styled";
import { useDevContext } from "../context/devContext";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { useBackupDeviceName } from "../hooks/useBackupDeviceName";
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
-import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { useLang } from "../hooks/useLang";
+// import { strings as messages } from "../i18n/strings";
import * as wxApi from "../wxApi";
export function SettingsPage(): VNode {
@@ -32,7 +31,7 @@ export function SettingsPage(): VNode {
const { devMode, toggleDevMode } = useDevContext();
const { name, update } = useBackupDeviceName();
const [lang, changeLang] = useLang();
- const exchangesHook = useAsyncAsHook(() => wxApi.listExchanges());
+ const exchangesHook = useAsyncAsHook(wxApi.listExchanges);
return (
<SettingsView
@@ -65,34 +64,32 @@ export interface ViewProps {
knownExchanges: Array<ExchangeListItem>;
}
-import { strings as messages } from "../i18n/strings";
-
-type LangsNames = {
- [P in keyof typeof messages]: string;
-};
+// type LangsNames = {
+// [P in keyof typeof messages]: string;
+// };
-const names: LangsNames = {
- es: "Español [es]",
- en: "English [en]",
- fr: "Français [fr]",
- de: "Deutsch [de]",
- sv: "Svenska [sv]",
- it: "Italiano [it]",
-};
+// const names: LangsNames = {
+// es: "Español [es]",
+// en: "English [en]",
+// fr: "Français [fr]",
+// de: "Deutsch [de]",
+// sv: "Svenska [sv]",
+// it: "Italiano [it]",
+// };
export function SettingsView({
knownExchanges,
- lang,
- changeLang,
- deviceName,
- setDeviceName,
+ // lang,
+ // changeLang,
+ // deviceName,
+ // setDeviceName,
permissionsEnabled,
togglePermissions,
developerMode,
toggleDeveloperMode,
}: ViewProps): VNode {
return (
- <WalletBox>
+ <Fragment>
<section>
<h2>
<i18n.Translate>Known exchanges</i18n.Translate>
@@ -100,17 +97,23 @@ export function SettingsView({
{!knownExchanges || !knownExchanges.length ? (
<div>No exchange yet!</div>
) : (
- <table>
- {knownExchanges.map((e) => (
- <tr>
- <td>{e.currency}</td>
- <td>
- <a href={e.exchangeBaseUrl}>{e.exchangeBaseUrl}</a>
- </td>
- </tr>
- ))}
- </table>
+ <Fragment>
+ <table>
+ {knownExchanges.map((e, idx) => (
+ <tr key={idx}>
+ <td>{e.currency}</td>
+ <td>
+ <a href={e.exchangeBaseUrl}>{e.exchangeBaseUrl}</a>
+ </td>
+ </tr>
+ ))}
+ </table>
+ </Fragment>
)}
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <div />
+ <ButtonPrimary>Manage exchange</ButtonPrimary>
+ </div>
<h2>
<i18n.Translate>Permissions</i18n.Translate>
@@ -131,6 +134,6 @@ export function SettingsView({
onToggle={toggleDeveloperMode}
/>
</section>
- </WalletBox>
+ </Fragment>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index c9a3f47cb..a25e2ca80 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -61,6 +61,7 @@ const exampleData = {
exchangeBaseUrl: "http://exchange.taler",
withdrawalDetails: {
confirmed: false,
+ reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
exchangePaytoUris: ["payto://x-taler-bank/bank/account"],
type: WithdrawalType.ManualTransfer,
},
@@ -134,10 +135,49 @@ export const WithdrawError = createExample(TestedComponent, {
},
});
-export const WithdrawPending = createExample(TestedComponent, {
- transaction: { ...exampleData.withdraw, pending: true },
+export const WithdrawPendingManual = createExample(TestedComponent, {
+ transaction: {
+ ...exampleData.withdraw,
+ withdrawalDetails: {
+ type: WithdrawalType.ManualTransfer,
+ exchangePaytoUris: ["payto://iban/asdasdasd"],
+ reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+ },
+ pending: true,
+ },
});
+export const WithdrawPendingTalerBankUnconfirmed = createExample(
+ TestedComponent,
+ {
+ transaction: {
+ ...exampleData.withdraw,
+ withdrawalDetails: {
+ type: WithdrawalType.TalerBankIntegrationApi,
+ confirmed: false,
+ reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+ bankConfirmationUrl: "http://bank.demo.taler.net",
+ },
+ pending: true,
+ },
+ },
+);
+
+export const WithdrawPendingTalerBankConfirmed = createExample(
+ TestedComponent,
+ {
+ transaction: {
+ ...exampleData.withdraw,
+ withdrawalDetails: {
+ type: WithdrawalType.TalerBankIntegrationApi,
+ confirmed: true,
+ reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+ },
+ pending: true,
+ },
+ },
+);
+
export const Payment = createExample(TestedComponent, {
transaction: exampleData.payment,
});
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 1472efb40..02c78320a 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -18,62 +18,80 @@ import {
AmountLike,
Amounts,
i18n,
+ NotificationType,
+ parsePaytoUri,
Transaction,
TransactionType,
+ WithdrawalType,
} from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
import { route } from "preact-router";
-import { useEffect, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
import emptyImg from "../../static/img/empty.png";
+import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType";
import { ErrorMessage } from "../components/ErrorMessage";
import { Part } from "../components/Part";
import {
Button,
ButtonDestructive,
ButtonPrimary,
+ CenteredDialog,
+ InfoBox,
ListOfProducts,
+ Overlay,
RowBorderGray,
SmallLightText,
- WalletBox,
WarningBox,
} from "../components/styled";
import { Time } from "../components/Time";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { Pages } from "../NavigationBar";
import * as wxApi from "../wxApi";
export function TransactionPage({ tid }: { tid: string }): VNode {
- const [transaction, setTransaction] = useState<Transaction | undefined>(
- undefined,
- );
+ async function getTransaction(): Promise<Transaction> {
+ const res = await wxApi.getTransactions();
+ const ts = res.transactions.filter((t) => t.transactionId === tid);
+ if (ts.length > 1) throw Error("more than one transaction with this id");
+ if (ts.length === 1) {
+ return ts[0];
+ }
+ throw Error("no transaction found");
+ }
- useEffect(() => {
- const fetchData = async (): Promise<void> => {
- const res = await wxApi.getTransactions();
- const ts = res.transactions.filter((t) => t.transactionId === tid);
- if (ts.length === 1) {
- setTransaction(ts[0]);
- } else {
- route(Pages.history);
- }
- };
- fetchData();
- }, [tid]);
+ const state = useAsyncAsHook(getTransaction, [
+ NotificationType.WithdrawGroupFinished,
+ ]);
- if (!transaction) {
+ if (!state) {
return (
<div>
<i18n.Translate>Loading ...</i18n.Translate>
</div>
);
}
+
+ if (state.hasError) {
+ route(Pages.history);
+ return (
+ <div>
+ <i18n.Translate>
+ There was an error. Redirecting into the history page
+ </i18n.Translate>
+ </div>
+ );
+ }
+
+ function goToHistory(): void {
+ route(Pages.history);
+ }
+
return (
<TransactionView
- transaction={transaction}
- onDelete={() => wxApi.deleteTransaction(tid).then(() => history.go(-1))}
- onRetry={() => wxApi.retryTransaction(tid).then(() => history.go(-1))}
- onBack={() => {
- route(Pages.history);
- }}
+ transaction={state.response}
+ onDelete={() => wxApi.deleteTransaction(tid).then(goToHistory)}
+ onRetry={() => wxApi.retryTransaction(tid).then(goToHistory)}
+ onBack={goToHistory}
/>
);
}
@@ -91,16 +109,28 @@ export function TransactionView({
onRetry,
onBack,
}: WalletTransactionProps): VNode {
- function TransactionTemplate({ children }: { children: VNode[] }): VNode {
+ const [confirmBeforeForget, setConfirmBeforeForget] = useState(false);
+ function doCheckBeforeForget(): void {
+ if (
+ transaction.pending &&
+ transaction.type === TransactionType.Withdrawal
+ ) {
+ setConfirmBeforeForget(true);
+ } else {
+ onDelete();
+ }
+ }
+ function TransactionTemplate({
+ children,
+ }: {
+ children: ComponentChildren;
+ }): VNode {
return (
- <WalletBox>
+ <Fragment>
<section style={{ padding: 8, textAlign: "center" }}>
<ErrorMessage title={transaction?.error?.hint} />
{transaction.pending && (
- <WarningBox>
- This transaction is not completed
- <a href="">more info...</a>
- </WarningBox>
+ <WarningBox>This transaction is not completed</WarningBox>
)}
</section>
<section>
@@ -116,12 +146,12 @@ export function TransactionView({
<i18n.Translate>retry</i18n.Translate>
</ButtonPrimary>
) : null}
- <ButtonDestructive onClick={onDelete}>
+ <ButtonDestructive onClick={doCheckBeforeForget}>
<i18n.Translate> Forget </i18n.Translate>
</ButtonDestructive>
</div>
</footer>
- </WalletBox>
+ </Fragment>
);
}
@@ -138,27 +168,119 @@ export function TransactionView({
).amount;
return (
<TransactionTemplate>
+ {confirmBeforeForget ? (
+ <Overlay>
+ <CenteredDialog>
+ <header>Caution!</header>
+ <section>
+ If you have already wired money to the exchange you will loose
+ the chance to get the coins form it.
+ </section>
+ <footer>
+ <Button onClick={() => setConfirmBeforeForget(false)}>
+ <i18n.Translate> Cancel </i18n.Translate>
+ </Button>
+
+ <ButtonDestructive onClick={onDelete}>
+ <i18n.Translate> Confirm </i18n.Translate>
+ </ButtonDestructive>
+ </footer>
+ </CenteredDialog>
+ </Overlay>
+ ) : undefined}
<h2>Withdrawal</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
- <br />
- <Part
- big
- title="Total withdrawn"
- text={amountToString(transaction.amountEffective)}
- kind="positive"
- />
- <Part
- big
- title="Chosen amount"
- text={amountToString(transaction.amountRaw)}
- kind="neutral"
- />
- <Part
- big
- title="Exchange fee"
- text={amountToString(fee)}
- kind="negative"
- />
+ {transaction.pending ? (
+ transaction.withdrawalDetails.type ===
+ WithdrawalType.ManualTransfer ? (
+ <Fragment>
+ <BankDetailsByPaytoType
+ amount={amountToString(transaction.amountRaw)}
+ exchangeBaseUrl={transaction.exchangeBaseUrl}
+ payto={parsePaytoUri(
+ transaction.withdrawalDetails.exchangePaytoUris[0],
+ )}
+ subject={transaction.withdrawalDetails.reservePub}
+ />
+ <p>
+ <WarningBox>
+ Make sure to use the correct subject, otherwise the money will
+ not arrive in this wallet.
+ </WarningBox>
+ </p>
+ <Part
+ big
+ title="Total withdrawn"
+ text={amountToString(transaction.amountEffective)}
+ kind="positive"
+ />
+ <Part
+ big
+ title="Exchange fee"
+ text={amountToString(fee)}
+ kind="negative"
+ />
+ </Fragment>
+ ) : (
+ <Fragment>
+ {!transaction.withdrawalDetails.confirmed &&
+ transaction.withdrawalDetails.bankConfirmationUrl ? (
+ <InfoBox>
+ The bank is waiting for confirmation. Go to the
+ <a
+ href={transaction.withdrawalDetails.bankConfirmationUrl}
+ target="_blank"
+ rel="noreferrer"
+ >
+ bank site
+ </a>
+ </InfoBox>
+ ) : undefined}
+ {transaction.withdrawalDetails.confirmed && (
+ <InfoBox>Waiting for the coins to arrive</InfoBox>
+ )}
+ <Part
+ big
+ title="Total withdrawn"
+ text={amountToString(transaction.amountEffective)}
+ kind="positive"
+ />
+ <Part
+ big
+ title="Chosen amount"
+ text={amountToString(transaction.amountRaw)}
+ kind="neutral"
+ />
+ <Part
+ big
+ title="Exchange fee"
+ text={amountToString(fee)}
+ kind="negative"
+ />
+ </Fragment>
+ )
+ ) : (
+ <Fragment>
+ <Part
+ big
+ title="Total withdrawn"
+ text={amountToString(transaction.amountEffective)}
+ kind="positive"
+ />
+ <Part
+ big
+ title="Chosen amount"
+ text={amountToString(transaction.amountRaw)}
+ kind="neutral"
+ />
+ <Part
+ big
+ title="Exchange fee"
+ text={amountToString(fee)}
+ kind="negative"
+ />
+ </Fragment>
+ )}
<Part
title="Exchange"
text={new URL(transaction.exchangeBaseUrl).hostname}
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index a6dd040e4..b180fdd05 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -20,13 +20,12 @@
* @author Florian Dold
*/
+import { WalletDiagnostics } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox";
-import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
import { Diagnostics } from "../components/Diagnostics";
-import { WalletBox } from "../components/styled";
import { useDiagnostics } from "../hooks/useDiagnostics";
-import { WalletDiagnostics } from "@gnu-taler/taler-util";
-import { h, VNode } from "preact";
+import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
export function WelcomePage(): VNode {
const [permissionsEnabled, togglePermissions] = useExtendedPermissions();
@@ -54,7 +53,7 @@ export function View({
timedOut,
}: ViewProps): VNode {
return (
- <WalletBox>
+ <Fragment>
<h1>Browser Extension Installed!</h1>
<div>
<p>Thank you for installing the wallet.</p>
@@ -75,6 +74,6 @@ export function View({
Learn how to top up your wallet balance »
</a>
</div>
- </WalletBox>
+ </Fragment>
);
}