diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/cta/Withdraw.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/cta/Withdraw.tsx | 383 |
1 files changed, 0 insertions, 383 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx deleted file mode 100644 index 6ef72cbe6..000000000 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ /dev/null @@ -1,383 +0,0 @@ -/* - This file is part of TALER - (C) 2015-2016 GNUnet e.V. - - 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. - - 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Page shown to the user to confirm creation - * of a reserve, usually requested by the bank. - * - * @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 { 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 { - acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, setExchangeTosAccepted, listExchanges, getExchangeTos -} from "../wxApi"; -import { wxMain } from '../wxBackend.js'; - -interface Props { - talerWithdrawUri?: string; -} - -export interface ViewProps { - details: GetExchangeTosResult; - withdrawalFee: AmountJson; - exchangeBaseUrl: string; - amount: AmountJson; - onSwitchExchange: (ex: string) => void; - onWithdraw: () => Promise<void>; - onReview: (b: boolean) => void; - onAccept: (b: boolean) => void; - reviewing: boolean; - reviewed: boolean; - confirmed: boolean; - terms: { - value?: TermsDocument; - status: TermsStatus; - }; - knownExchanges: ExchangeListItem[]; - -}; - -type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound'; - -type TermsDocument = TermsDocumentXml | TermsDocumentHtml | TermsDocumentPlain | TermsDocumentJson | TermsDocumentPdf; - -interface TermsDocumentXml { - type: 'xml', - document: Document, -} - -interface TermsDocumentHtml { - type: 'html', - href: URL, -} - -interface TermsDocumentPlain { - type: 'plain', - content: string, -} - -interface TermsDocumentJson { - type: 'json', - data: any, -} - -interface TermsDocumentPdf { - type: 'pdf', - location: URL, -} - -function amountToString(text: AmountJson) { - 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 }), {}) - - return ( - <WalletAction> - <LogoHeader /> - <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 /> - </section> - {!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("")}> - {i18n.str`Switch exchange`} - </LinkSuccess>} - - </section> - } - {!reviewing && reviewed && - <section> - <LinkSuccess - upperCased - onClick={() => onReview(true)} - > - {i18n.str`Show terms of service`} - </LinkSuccess> - </section> - } - {terms.status === 'notfound' && - <section> - <WarningText> - {i18n.str`Exchange doesn't have terms of service`} - </WarningText> - </section> - } - {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 && - <section> - <LinkSuccess - upperCased - onClick={() => onReview(false)} - > - {i18n.str`Hide terms of service`} - </LinkSuccess> - </section> - } - {(reviewing || reviewed) && - <section> - <CheckboxOutlined - name="terms" - enabled={reviewed} - label={i18n.str`I accept the exchange terms of service`} - onToggle={() => { - onAccept(!reviewed) - onReview(false) - }} - /> - </section> - } - - {/** - * Main action section - */} - <section> - {terms.status === 'new' && !reviewed && !reviewing && - <ButtonSuccess - upperCased - disabled={!exchangeBaseUrl} - onClick={() => onReview(true)} - > - {i18n.str`Review exchange terms of service`} - </ButtonSuccess> - } - {terms.status === 'changed' && !reviewed && !reviewing && - <ButtonWarning - upperCased - disabled={!exchangeBaseUrl} - onClick={() => onReview(true)} - > - {i18n.str`Review new version of terms of service`} - </ButtonWarning> - } - {(terms.status === 'accepted' || (needsReview && reviewed)) && - <ButtonSuccess - upperCased - disabled={!exchangeBaseUrl || confirmed} - onClick={onWithdraw} - > - {i18n.str`Confirm withdrawal`} - </ButtonSuccess> - } - {terms.status === 'notfound' && - <ButtonWarning - upperCased - disabled={!exchangeBaseUrl} - onClick={onWithdraw} - > - {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 - const detailsHook = useAsyncAsHook(async () => { - 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 } - }) - - if (!detailsHook) { - 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>; - } - - const details = detailsHook.response - - const onAccept = async (): Promise<void> => { - try { - await setExchangeTosAccepted(exchange, details.tos.currentEtag) - setReviewed(true) - } catch (e) { - if (e instanceof Error) { - setErrorAccepting(e.message) - } - } - } - - const onWithdraw = async (): Promise<void> => { - setConfirmed(true) - console.log("accepting exchange", exchange); - try { - const res = await acceptWithdrawal(uri, exchange); - console.log("accept withdrawal response", res); - if (res.confirmTransferUrl) { - document.location.href = res.confirmTransferUrl; - } - } catch (e) { - 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} - /> -} -export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element { - const uriInfoHook = useAsyncAsHook(() => !talerWithdrawUri ? Promise.reject(undefined) : - getWithdrawalDetailsForUri({ talerWithdrawUri }) - ) - - if (!talerWithdrawUri) { - return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; - } - if (!uriInfoHook) { - 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 <WithdrawPageWithParsedURI uri={talerWithdrawUri} uriInfo={uriInfoHook.response} /> -} - -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 } - } catch (e) { - console.log(e) - debugger; - } - } else if (type === 'text/html') { - try { - const href = new URL(text) - return { type: 'html', href } - } catch (e) { - console.log(e) - debugger; - } - } else if (type === 'text/json') { - try { - const data = JSON.parse(text) - return { type: 'json', data } - } catch (e) { - console.log(e) - debugger; - } - } else if (type === 'text/pdf') { - try { - const location = new URL(text) - return { type: 'pdf', location } - } catch (e) { - console.log(e) - debugger; - } - } else if (type === 'text/plain') { - try { - const content = text - return { type: 'plain', content } - } catch (e) { - console.log(e) - debugger; - } - } - return undefined -} - |