summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-11-15 11:18:58 -0300
committerSebastian <sebasjm@gmail.com>2021-11-15 11:18:58 -0300
commit1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5 (patch)
tree99e8241a5eb5af4d752be93a460004bc0c6255aa /packages/taler-wallet-webextension/src/cta/Withdraw.tsx
parent9692f589c687a2ba39a705ca4238cf123f444c61 (diff)
downloadwallet-core-1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5.tar.gz
wallet-core-1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5.tar.bz2
wallet-core-1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5.zip
prettier
Diffstat (limited to 'packages/taler-wallet-webextension/src/cta/Withdraw.tsx')
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw.tsx500
1 files changed, 312 insertions, 188 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
index 6ef72cbe6..603dafcde 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
@@ -21,21 +21,39 @@
* @author Florian Dold
*/
-import { AmountJson, Amounts, ExchangeListItem, GetExchangeTosResult, i18n, WithdrawUriInfoResponse } from '@gnu-taler/taler-util';
-import { ExchangeWithdrawDetails } from '@gnu-taler/taler-wallet-core/src/operations/withdraw';
+import {
+ AmountJson,
+ Amounts,
+ ExchangeListItem,
+ GetExchangeTosResult,
+ i18n,
+ WithdrawUriInfoResponse,
+} from "@gnu-taler/taler-util";
+import { VNode, h } from "preact";
import { useState } from "preact/hooks";
-import { Fragment } from 'preact/jsx-runtime';
-import { CheckboxOutlined } from '../components/CheckboxOutlined';
-import { ExchangeXmlTos } from '../components/ExchangeToS';
-import { LogoHeader } from '../components/LogoHeader';
-import { Part } from '../components/Part';
-import { SelectList } from '../components/SelectList';
-import { ButtonSuccess, ButtonWarning, LinkSuccess, LinkWarning, TermsOfService, WalletAction, WarningText } from '../components/styled';
-import { useAsyncAsHook } from '../hooks/useAsyncAsHook';
+import { Fragment } from "preact/jsx-runtime";
+import { CheckboxOutlined } from "../components/CheckboxOutlined";
+import { ExchangeXmlTos } from "../components/ExchangeToS";
+import { LogoHeader } from "../components/LogoHeader";
+import { Part } from "../components/Part";
+import { SelectList } from "../components/SelectList";
+import {
+ ButtonSuccess,
+ ButtonWarning,
+ LinkSuccess,
+ TermsOfService,
+ WalletAction,
+ WarningText,
+} from "../components/styled";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import {
- acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, setExchangeTosAccepted, listExchanges, getExchangeTos
+ acceptWithdrawal,
+ getExchangeTos,
+ getExchangeWithdrawalInfo,
+ getWithdrawalDetailsForUri,
+ listExchanges,
+ setExchangeTosAccepted,
} from "../wxApi";
-import { wxMain } from '../wxBackend.js';
interface Props {
talerWithdrawUri?: string;
@@ -58,145 +76,193 @@ export interface ViewProps {
status: TermsStatus;
};
knownExchanges: ExchangeListItem[];
+}
-};
-
-type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound';
+type TermsStatus = "new" | "accepted" | "changed" | "notfound";
-type TermsDocument = TermsDocumentXml | TermsDocumentHtml | TermsDocumentPlain | TermsDocumentJson | TermsDocumentPdf;
+type TermsDocument =
+ | TermsDocumentXml
+ | TermsDocumentHtml
+ | TermsDocumentPlain
+ | TermsDocumentJson
+ | TermsDocumentPdf;
interface TermsDocumentXml {
- type: 'xml',
- document: Document,
+ type: "xml";
+ document: Document;
}
interface TermsDocumentHtml {
- type: 'html',
- href: URL,
+ type: "html";
+ href: URL;
}
interface TermsDocumentPlain {
- type: 'plain',
- content: string,
+ type: "plain";
+ content: string;
}
interface TermsDocumentJson {
- type: 'json',
- data: any,
+ type: "json";
+ data: any;
}
interface TermsDocumentPdf {
- type: 'pdf',
- location: URL,
+ type: "pdf";
+ location: URL;
}
function amountToString(text: AmountJson) {
- const aj = Amounts.jsonifyAmount(text)
- const amount = Amounts.stringifyValue(aj)
- return `${amount} ${aj.currency}`
+ const aj = Amounts.jsonifyAmount(text);
+ const amount = Amounts.stringifyValue(aj);
+ return `${amount} ${aj.currency}`;
}
-export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, reviewed, confirmed }: ViewProps) {
- const needsReview = terms.status === 'changed' || terms.status === 'new'
-
- const [switchingExchange, setSwitchingExchange] = useState<string | undefined>(undefined)
- const exchanges = knownExchanges.reduce((prev, ex) => ({ ...prev, [ex.exchangeBaseUrl]: ex.exchangeBaseUrl }), {})
+export function View({
+ details,
+ withdrawalFee,
+ exchangeBaseUrl,
+ knownExchanges,
+ amount,
+ onWithdraw,
+ onSwitchExchange,
+ terms,
+ reviewing,
+ onReview,
+ onAccept,
+ reviewed,
+ confirmed,
+}: ViewProps) {
+ const needsReview = terms.status === "changed" || terms.status === "new";
+
+ const [switchingExchange, setSwitchingExchange] = useState<
+ string | undefined
+ >(undefined);
+ const exchanges = knownExchanges.reduce(
+ (prev, ex) => ({ ...prev, [ex.exchangeBaseUrl]: ex.exchangeBaseUrl }),
+ {},
+ );
return (
<WalletAction>
<LogoHeader />
- <h2>
- {i18n.str`Digital cash withdrawal`}
- </h2>
+ <h2>{i18n.str`Digital cash withdrawal`}</h2>
<section>
- <Part title="Total to withdraw" text={amountToString(Amounts.sub(amount, withdrawalFee).amount)} kind='positive' />
- <Part title="Chosen amount" text={amountToString(amount)} kind='neutral' />
- {Amounts.isNonZero(withdrawalFee) &&
- <Part title="Exchange fee" text={amountToString(withdrawalFee)} kind='negative' />
- }
- <Part title="Exchange" text={exchangeBaseUrl} kind='neutral' big />
+ <Part
+ title="Total to withdraw"
+ text={amountToString(Amounts.sub(amount, withdrawalFee).amount)}
+ kind="positive"
+ />
+ <Part
+ title="Chosen amount"
+ text={amountToString(amount)}
+ kind="neutral"
+ />
+ {Amounts.isNonZero(withdrawalFee) && (
+ <Part
+ title="Exchange fee"
+ text={amountToString(withdrawalFee)}
+ kind="negative"
+ />
+ )}
+ <Part title="Exchange" text={exchangeBaseUrl} kind="neutral" big />
</section>
- {!reviewing &&
+ {!reviewing && (
<section>
- {switchingExchange !== undefined ? <Fragment>
- <div>
- <SelectList label="Known exchanges" list={exchanges} name="" onChange={onSwitchExchange} />
- </div>
- <LinkSuccess upperCased onClick={() => onSwitchExchange(switchingExchange)}>
- {i18n.str`Confirm exchange selection`}
- </LinkSuccess>
- </Fragment>
- : <LinkSuccess upperCased onClick={() => setSwitchingExchange("")}>
+ {switchingExchange !== undefined ? (
+ <Fragment>
+ <div>
+ <SelectList
+ label="Known exchanges"
+ list={exchanges}
+ name=""
+ onChange={onSwitchExchange}
+ />
+ </div>
+ <LinkSuccess
+ upperCased
+ onClick={() => onSwitchExchange(switchingExchange)}
+ >
+ {i18n.str`Confirm exchange selection`}
+ </LinkSuccess>
+ </Fragment>
+ ) : (
+ <LinkSuccess upperCased onClick={() => setSwitchingExchange("")}>
{i18n.str`Switch exchange`}
- </LinkSuccess>}
-
+ </LinkSuccess>
+ )}
</section>
- }
- {!reviewing && reviewed &&
+ )}
+ {!reviewing && reviewed && (
<section>
- <LinkSuccess
- upperCased
- onClick={() => onReview(true)}
- >
+ <LinkSuccess upperCased onClick={() => onReview(true)}>
{i18n.str`Show terms of service`}
</LinkSuccess>
</section>
- }
- {terms.status === 'notfound' &&
+ )}
+ {terms.status === "notfound" && (
<section>
<WarningText>
{i18n.str`Exchange doesn't have terms of service`}
</WarningText>
</section>
- }
- {reviewing &&
+ )}
+ {reviewing && (
<section>
- {terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' &&
- <TermsOfService>
- <ExchangeXmlTos doc={terms.value.document} />
- </TermsOfService>
- }
- {terms.status !== 'accepted' && terms.value && terms.value.type === 'plain' &&
- <div style={{ textAlign: 'left' }}>
- <pre>{terms.value.content}</pre>
- </div>
- }
- {terms.status !== 'accepted' && terms.value && terms.value.type === 'html' &&
- <iframe src={terms.value.href.toString()} />
- }
- {terms.status !== 'accepted' && terms.value && terms.value.type === 'pdf' &&
- <a href={terms.value.location.toString()} download="tos.pdf" >Download Terms of Service</a>
- }
- </section>}
- {reviewing && reviewed &&
+ {terms.status !== "accepted" &&
+ terms.value &&
+ terms.value.type === "xml" && (
+ <TermsOfService>
+ <ExchangeXmlTos doc={terms.value.document} />
+ </TermsOfService>
+ )}
+ {terms.status !== "accepted" &&
+ terms.value &&
+ terms.value.type === "plain" && (
+ <div style={{ textAlign: "left" }}>
+ <pre>{terms.value.content}</pre>
+ </div>
+ )}
+ {terms.status !== "accepted" &&
+ terms.value &&
+ terms.value.type === "html" && (
+ <iframe src={terms.value.href.toString()} />
+ )}
+ {terms.status !== "accepted" &&
+ terms.value &&
+ terms.value.type === "pdf" && (
+ <a href={terms.value.location.toString()} download="tos.pdf">
+ Download Terms of Service
+ </a>
+ )}
+ </section>
+ )}
+ {reviewing && reviewed && (
<section>
- <LinkSuccess
- upperCased
- onClick={() => onReview(false)}
- >
+ <LinkSuccess upperCased onClick={() => onReview(false)}>
{i18n.str`Hide terms of service`}
</LinkSuccess>
</section>
- }
- {(reviewing || reviewed) &&
+ )}
+ {(reviewing || reviewed) && (
<section>
<CheckboxOutlined
name="terms"
enabled={reviewed}
label={i18n.str`I accept the exchange terms of service`}
onToggle={() => {
- onAccept(!reviewed)
- onReview(false)
+ onAccept(!reviewed);
+ onReview(false);
}}
/>
</section>
- }
+ )}
{/**
* Main action section
*/}
<section>
- {terms.status === 'new' && !reviewed && !reviewing &&
+ {terms.status === "new" && !reviewed && !reviewing && (
<ButtonSuccess
upperCased
disabled={!exchangeBaseUrl}
@@ -204,8 +270,8 @@ export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges,
>
{i18n.str`Review exchange terms of service`}
</ButtonSuccess>
- }
- {terms.status === 'changed' && !reviewed && !reviewing &&
+ )}
+ {terms.status === "changed" && !reviewed && !reviewing && (
<ButtonWarning
upperCased
disabled={!exchangeBaseUrl}
@@ -213,8 +279,8 @@ export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges,
>
{i18n.str`Review new version of terms of service`}
</ButtonWarning>
- }
- {(terms.status === 'accepted' || (needsReview && reviewed)) &&
+ )}
+ {(terms.status === "accepted" || (needsReview && reviewed)) && (
<ButtonSuccess
upperCased
disabled={!exchangeBaseUrl || confirmed}
@@ -222,8 +288,8 @@ export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges,
>
{i18n.str`Confirm withdrawal`}
</ButtonSuccess>
- }
- {terms.status === 'notfound' &&
+ )}
+ {terms.status === "notfound" && (
<ButtonWarning
upperCased
disabled={!exchangeBaseUrl}
@@ -231,60 +297,88 @@ export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges,
>
{i18n.str`Withdraw anyway`}
</ButtonWarning>
- }
+ )}
</section>
</WalletAction>
- )
+ );
}
-export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriInfo: WithdrawUriInfoResponse }) {
- const [customExchange, setCustomExchange] = useState<string | undefined>(undefined)
- const [errorAccepting, setErrorAccepting] = useState<string | undefined>(undefined)
-
- const [reviewing, setReviewing] = useState<boolean>(false)
- const [reviewed, setReviewed] = useState<boolean>(false)
- const [confirmed, setConfirmed] = useState<boolean>(false)
-
- const knownExchangesHook = useAsyncAsHook(() => listExchanges())
-
- const knownExchanges = !knownExchangesHook || knownExchangesHook.hasError ? [] : knownExchangesHook.response.exchanges
- const withdrawAmount = Amounts.parseOrThrow(uriInfo.amount)
- const thisCurrencyExchanges = knownExchanges.filter(ex => ex.currency === withdrawAmount.currency)
-
- const exchange = customExchange || uriInfo.defaultExchangeBaseUrl || thisCurrencyExchanges[0]?.exchangeBaseUrl
+export function WithdrawPageWithParsedURI({
+ uri,
+ uriInfo,
+}: {
+ uri: string;
+ uriInfo: WithdrawUriInfoResponse;
+}) {
+ const [customExchange, setCustomExchange] = useState<string | undefined>(
+ undefined,
+ );
+ const [errorAccepting, setErrorAccepting] = useState<string | undefined>(
+ undefined,
+ );
+
+ const [reviewing, setReviewing] = useState<boolean>(false);
+ const [reviewed, setReviewed] = useState<boolean>(false);
+ const [confirmed, setConfirmed] = useState<boolean>(false);
+
+ const knownExchangesHook = useAsyncAsHook(() => listExchanges());
+
+ const knownExchanges =
+ !knownExchangesHook || knownExchangesHook.hasError
+ ? []
+ : knownExchangesHook.response.exchanges;
+ const withdrawAmount = Amounts.parseOrThrow(uriInfo.amount);
+ const thisCurrencyExchanges = knownExchanges.filter(
+ (ex) => ex.currency === withdrawAmount.currency,
+ );
+
+ const exchange =
+ customExchange ||
+ uriInfo.defaultExchangeBaseUrl ||
+ thisCurrencyExchanges[0]?.exchangeBaseUrl;
const detailsHook = useAsyncAsHook(async () => {
- if (!exchange) throw Error('no default exchange')
- const tos = await getExchangeTos(exchange, ['text/xml'])
+ if (!exchange) throw Error("no default exchange");
+ const tos = await getExchangeTos(exchange, ["text/xml"]);
const info = await getExchangeWithdrawalInfo({
exchangeBaseUrl: exchange,
amount: withdrawAmount,
- tosAcceptedFormat: ['text/xml']
- })
- return { tos, info }
- })
+ tosAcceptedFormat: ["text/xml"],
+ });
+ return { tos, info };
+ });
if (!detailsHook) {
- return <span><i18n.Translate>Getting withdrawal details.</i18n.Translate></span>;
+ return (
+ <span>
+ <i18n.Translate>Getting withdrawal details.</i18n.Translate>
+ </span>
+ );
}
if (detailsHook.hasError) {
- return <span><i18n.Translate>Problems getting details: {detailsHook.message}</i18n.Translate></span>;
+ return (
+ <span>
+ <i18n.Translate>
+ Problems getting details: {detailsHook.message}
+ </i18n.Translate>
+ </span>
+ );
}
- const details = detailsHook.response
+ const details = detailsHook.response;
const onAccept = async (): Promise<void> => {
try {
- await setExchangeTosAccepted(exchange, details.tos.currentEtag)
- setReviewed(true)
+ await setExchangeTosAccepted(exchange, details.tos.currentEtag);
+ setReviewed(true);
} catch (e) {
if (e instanceof Error) {
- setErrorAccepting(e.message)
+ setErrorAccepting(e.message);
}
}
- }
+ };
const onWithdraw = async (): Promise<void> => {
- setConfirmed(true)
+ setConfirmed(true);
console.log("accepting exchange", exchange);
try {
const res = await acceptWithdrawal(uri, exchange);
@@ -293,91 +387,121 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
document.location.href = res.confirmTransferUrl;
}
} catch (e) {
- setConfirmed(false)
+ setConfirmed(false);
}
};
- const termsContent: TermsDocument | undefined = parseTermsOfServiceContent(details.tos.contentType, details.tos.content);
-
- const status: TermsStatus = !termsContent ? 'notfound' : (
- !details.tos.acceptedEtag ? 'new' : (
- details.tos.acceptedEtag !== details.tos.currentEtag ? 'changed' : 'accepted'
- ))
-
-
- return <View onWithdraw={onWithdraw}
- details={details.tos} amount={withdrawAmount}
- exchangeBaseUrl={exchange}
- withdrawalFee={details.info.withdrawFee} //FIXME
- terms={{
- status, value: termsContent
- }}
- onSwitchExchange={setCustomExchange}
- knownExchanges={knownExchanges}
- confirmed={confirmed}
- reviewed={reviewed} onAccept={onAccept}
- reviewing={reviewing} onReview={setReviewing}
- />
+ const termsContent: TermsDocument | undefined = parseTermsOfServiceContent(
+ details.tos.contentType,
+ details.tos.content,
+ );
+
+ const status: TermsStatus = !termsContent
+ ? "notfound"
+ : !details.tos.acceptedEtag
+ ? "new"
+ : details.tos.acceptedEtag !== details.tos.currentEtag
+ ? "changed"
+ : "accepted";
+
+ return (
+ <View
+ onWithdraw={onWithdraw}
+ details={details.tos}
+ amount={withdrawAmount}
+ exchangeBaseUrl={exchange}
+ withdrawalFee={details.info.withdrawFee} //FIXME
+ terms={{
+ status,
+ value: termsContent,
+ }}
+ onSwitchExchange={setCustomExchange}
+ knownExchanges={knownExchanges}
+ confirmed={confirmed}
+ reviewed={reviewed}
+ onAccept={onAccept}
+ reviewing={reviewing}
+ onReview={setReviewing}
+ />
+ );
}
-export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
- const uriInfoHook = useAsyncAsHook(() => !talerWithdrawUri ? Promise.reject(undefined) :
- getWithdrawalDetailsForUri({ talerWithdrawUri })
- )
+export function WithdrawPage({ talerWithdrawUri }: Props): VNode {
+ const uriInfoHook = useAsyncAsHook(() =>
+ !talerWithdrawUri
+ ? Promise.reject(undefined)
+ : getWithdrawalDetailsForUri({ talerWithdrawUri }),
+ );
if (!talerWithdrawUri) {
- return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>;
+ return (
+ <span>
+ <i18n.Translate>missing withdraw uri</i18n.Translate>
+ </span>
+ );
}
if (!uriInfoHook) {
- return <span><i18n.Translate>Loading...</i18n.Translate></span>;
+ return (
+ <span>
+ <i18n.Translate>Loading...</i18n.Translate>
+ </span>
+ );
}
if (uriInfoHook.hasError) {
- return <span><i18n.Translate>This URI is not valid anymore: {uriInfoHook.message}</i18n.Translate></span>;
+ return (
+ <span>
+ <i18n.Translate>
+ This URI is not valid anymore: {uriInfoHook.message}
+ </i18n.Translate>
+ </span>
+ );
}
- return <WithdrawPageWithParsedURI uri={talerWithdrawUri} uriInfo={uriInfoHook.response} />
+ return (
+ <WithdrawPageWithParsedURI
+ uri={talerWithdrawUri}
+ uriInfo={uriInfoHook.response}
+ />
+ );
}
-function parseTermsOfServiceContent(type: string, text: string): TermsDocument | undefined {
- if (type === 'text/xml') {
+function parseTermsOfServiceContent(
+ type: string,
+ text: string,
+): TermsDocument | undefined {
+ if (type === "text/xml") {
try {
- const document = new DOMParser().parseFromString(text, "text/xml")
- return { type: 'xml', document }
+ const document = new DOMParser().parseFromString(text, "text/xml");
+ return { type: "xml", document };
} catch (e) {
- console.log(e)
- debugger;
+ console.log(e);
}
- } else if (type === 'text/html') {
+ } else if (type === "text/html") {
try {
- const href = new URL(text)
- return { type: 'html', href }
+ const href = new URL(text);
+ return { type: "html", href };
} catch (e) {
- console.log(e)
- debugger;
+ console.log(e);
}
- } else if (type === 'text/json') {
+ } else if (type === "text/json") {
try {
- const data = JSON.parse(text)
- return { type: 'json', data }
+ const data = JSON.parse(text);
+ return { type: "json", data };
} catch (e) {
- console.log(e)
- debugger;
+ console.log(e);
}
- } else if (type === 'text/pdf') {
+ } else if (type === "text/pdf") {
try {
- const location = new URL(text)
- return { type: 'pdf', location }
+ const location = new URL(text);
+ return { type: "pdf", location };
} catch (e) {
- console.log(e)
- debugger;
+ console.log(e);
}
- } else if (type === 'text/plain') {
+ } else if (type === "text/plain") {
try {
- const content = text
- return { type: 'plain', content }
+ const content = text;
+ return { type: "plain", content };
} catch (e) {
- console.log(e)
- debugger;
+ console.log(e);
}
}
- return undefined
+ return undefined;
}
-