diff options
Diffstat (limited to 'src/webex/pages')
-rw-r--r-- | src/webex/pages/add-auditor.tsx | 135 | ||||
-rw-r--r-- | src/webex/pages/auditors.tsx | 161 | ||||
-rw-r--r-- | src/webex/pages/benchmark.tsx | 104 | ||||
-rw-r--r-- | src/webex/pages/pay.tsx | 182 | ||||
-rw-r--r-- | src/webex/pages/payback.tsx | 30 | ||||
-rw-r--r-- | src/webex/pages/popup.tsx | 499 | ||||
-rw-r--r-- | src/webex/pages/refund.tsx | 89 | ||||
-rw-r--r-- | src/webex/pages/reset-required.tsx | 93 | ||||
-rw-r--r-- | src/webex/pages/return-coins.tsx | 30 | ||||
-rw-r--r-- | src/webex/pages/tip.tsx | 103 | ||||
-rw-r--r-- | src/webex/pages/welcome.tsx | 190 | ||||
-rw-r--r-- | src/webex/pages/withdraw.tsx | 229 |
12 files changed, 0 insertions, 1845 deletions
diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx deleted file mode 100644 index c28d15cad..000000000 --- a/src/webex/pages/add-auditor.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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/> - */ - -/** - * View and edit auditors. - * - * @author Florian Dold - */ - -import { CurrencyRecord } from "../../types/dbTypes"; -import { getCurrencies, updateCurrency } from "../wxApi"; -import React, { useState } from "react"; - -interface ConfirmAuditorProps { - url: string; - currency: string; - auditorPub: string; - expirationStamp: number; -} - -function ConfirmAuditor(props: ConfirmAuditorProps): JSX.Element { - const [addDone, setAddDone] = useState(false); - - const add = async (): Promise<void> => { - const currencies = await getCurrencies(); - let currency: CurrencyRecord | undefined; - - for (const c of currencies) { - if (c.name === props.currency) { - currency = c; - } - } - - if (!currency) { - currency = { - name: props.currency, - auditors: [], - fractionalDigits: 2, - exchanges: [], - }; - } - - const newAuditor = { - auditorPub: props.auditorPub, - baseUrl: props.url, - expirationStamp: props.expirationStamp, - }; - - let auditorFound = false; - for (const idx in currency.auditors) { - const a = currency.auditors[idx]; - if (a.baseUrl === props.url) { - auditorFound = true; - // Update auditor if already found by URL. - currency.auditors[idx] = newAuditor; - } - } - - if (!auditorFound) { - currency.auditors.push(newAuditor); - } - - await updateCurrency(currency); - - setAddDone(true); - }; - - const back = (): void => { - window.history.back(); - }; - - return ( - <div id="main"> - <p> - Do you want to let <strong>{props.auditorPub}</strong> audit the - currency "{props.currency}"? - </p> - {addDone ? ( - <div> - Auditor was added! You can also{" "} - <a href={chrome.extension.getURL("/auditors.html")}>view and edit</a>{" "} - auditors. - </div> - ) : ( - <div> - <button - onClick={() => add()} - className="pure-button pure-button-primary" - > - Yes - </button> - <button onClick={() => back()} className="pure-button"> - No - </button> - </div> - )} - </div> - ); -} - -export function makeAddAuditorPage(): JSX.Element { - const walletPageUrl = new URL(document.location.href); - const url = walletPageUrl.searchParams.get("url"); - if (!url) { - throw Error("missign parameter (url)"); - } - const currency = walletPageUrl.searchParams.get("currency"); - if (!currency) { - throw Error("missing parameter (currency)"); - } - const auditorPub = walletPageUrl.searchParams.get("auditorPub"); - if (!auditorPub) { - throw Error("missing parameter (auditorPub)"); - } - const auditorStampStr = walletPageUrl.searchParams.get("expirationStamp"); - if (!auditorStampStr) { - throw Error("missing parameter (auditorStampStr)"); - } - const expirationStamp = Number.parseInt(auditorStampStr); - const args = { url, currency, auditorPub, expirationStamp }; - return <ConfirmAuditor {...args} />; -} diff --git a/src/webex/pages/auditors.tsx b/src/webex/pages/auditors.tsx deleted file mode 100644 index ac93afd31..000000000 --- a/src/webex/pages/auditors.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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/> - */ - -/** - * View and edit auditors. - * - * @author Florian Dold - */ - -import { - AuditorRecord, - CurrencyRecord, - ExchangeForCurrencyRecord, -} from "../../types/dbTypes"; - -import { getCurrencies, updateCurrency } from "../wxApi"; - -import * as React from "react"; - -interface CurrencyListState { - currencies?: CurrencyRecord[]; -} - -class CurrencyList extends React.Component<{}, CurrencyListState> { - constructor(props: {}) { - super(props); - const port = chrome.runtime.connect(); - port.onMessage.addListener((msg: any) => { - if (msg.notify) { - console.log("got notified"); - this.update(); - } - }); - this.update(); - this.state = {} as any; - } - - async update(): Promise<void> { - const currencies = await getCurrencies(); - console.log("currencies: ", currencies); - this.setState({ currencies }); - } - - async confirmRemoveAuditor( - c: CurrencyRecord, - a: AuditorRecord, - ): Promise<void> { - if ( - window.confirm( - `Do you really want to remove auditor ${a.baseUrl} for currency ${c.name}?`, - ) - ) { - c.auditors = c.auditors.filter((x) => x.auditorPub !== a.auditorPub); - await updateCurrency(c); - } - } - - async confirmRemoveExchange( - c: CurrencyRecord, - e: ExchangeForCurrencyRecord, - ): Promise<void> { - if ( - window.confirm( - `Do you really want to remove exchange ${e.baseUrl} for currency ${c.name}?`, - ) - ) { - c.exchanges = c.exchanges.filter((x) => x.baseUrl !== e.baseUrl); - await updateCurrency(c); - } - } - - renderAuditors(c: CurrencyRecord): any { - if (c.auditors.length === 0) { - return <p>No trusted auditors for this currency.</p>; - } - return ( - <div> - <p>Trusted Auditors:</p> - <ul> - {c.auditors.map((a) => ( - <li key={a.baseUrl}> - {a.baseUrl}{" "} - <button - className="pure-button button-destructive" - onClick={() => this.confirmRemoveAuditor(c, a)} - > - Remove - </button> - <ul> - <li>valid until {new Date(a.expirationStamp).toString()}</li> - <li>public key {a.auditorPub}</li> - </ul> - </li> - ))} - </ul> - </div> - ); - } - - renderExchanges(c: CurrencyRecord): any { - if (c.exchanges.length === 0) { - return <p>No trusted exchanges for this currency.</p>; - } - return ( - <div> - <p>Trusted Exchanges:</p> - <ul> - {c.exchanges.map((e) => ( - <li key={e.baseUrl}> - {e.baseUrl}{" "} - <button - className="pure-button button-destructive" - onClick={() => this.confirmRemoveExchange(c, e)} - > - Remove - </button> - </li> - ))} - </ul> - </div> - ); - } - - render(): JSX.Element { - const currencies = this.state.currencies; - if (!currencies) { - return <span>...</span>; - } - return ( - <div id="main"> - {currencies.map((c) => ( - <div key={c.name}> - <h1>Currency {c.name}</h1> - <p>Displayed with {c.fractionalDigits} fractional digits.</p> - <h2>Auditors</h2> - <div>{this.renderAuditors(c)}</div> - <h2>Exchanges</h2> - <div>{this.renderExchanges(c)}</div> - </div> - ))} - </div> - ); - } -} - -export function makeAuditorsPage(): JSX.Element { - return <CurrencyList />; -} diff --git a/src/webex/pages/benchmark.tsx b/src/webex/pages/benchmark.tsx deleted file mode 100644 index eb7193e0c..000000000 --- a/src/webex/pages/benchmark.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/* - This file is part of TALER - (C) 2015 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/> - */ - -/** - * Benchmarks for the wallet. - * - * @author Florian Dold - */ - -import * as i18n from "../i18n"; - -import { BenchmarkResult } from "../../types/walletTypes"; - -import * as wxApi from "../wxApi"; - -import * as React from "react"; - -interface BenchmarkRunnerState { - repetitions: number; - result?: BenchmarkResult; - running: boolean; -} - -function BenchmarkDisplay(props: BenchmarkRunnerState): JSX.Element { - const result = props.result; - if (!result) { - if (props.running) { - return <div>Waiting for results ...</div>; - } else { - return <div></div>; - } - } - return ( - <> - <h2>Results for {result.repetitions} repetitions</h2> - <table className="pure-table"> - <thead> - <tr> - <th>{i18n.str`Operation`}</th> - <th>{i18n.str`time (ms/op)`}</th> - </tr> - {Object.keys(result.time) - .sort() - .map((k) => ( - <tr key={k}> - <td>{k}</td> - <td>{result.time[k] / result.repetitions}</td> - </tr> - ))} - </thead> - </table> - </> - ); -} - -class BenchmarkRunner extends React.Component<any, BenchmarkRunnerState> { - constructor(props: any) { - super(props); - this.state = { - repetitions: 10, - running: false, - }; - } - - async run(): Promise<void> { - this.setState({ result: undefined, running: true }); - const result = await wxApi.benchmarkCrypto(this.state.repetitions); - this.setState({ result, running: false }); - } - - render(): JSX.Element { - return ( - <div> - <label>Repetitions:</label> - <input - type="number" - value={this.state.repetitions} - onChange={(evt) => - this.setState({ repetitions: Number.parseInt(evt.target.value) }) - } - />{" "} - <button onClick={() => this.run()}>Run</button> - <BenchmarkDisplay {...this.state} /> - </div> - ); - } -} - -export function makeBenchmarkPage(): JSX.Element { - return <BenchmarkRunner />; -} diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx deleted file mode 100644 index ce44c0040..000000000 --- a/src/webex/pages/pay.tsx +++ /dev/null @@ -1,182 +0,0 @@ -/* - This file is part of TALER - (C) 2015 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 entering - * a contract. - */ - -/** - * Imports. - */ -import * as i18n from "../i18n"; - -import { PreparePayResult, PreparePayResultType } from "../../types/walletTypes"; - -import { renderAmount, ProgressButton } from "../renderHtml"; -import * as wxApi from "../wxApi"; - -import React, { useState, useEffect } from "react"; - -import * as Amounts from "../../util/amounts"; -import { codecForContractTerms, ContractTerms } from "../../types/talerTypes"; - -function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element { - const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(); - const [payErrMsg, setPayErrMsg] = useState<string | undefined>(""); - const [numTries, setNumTries] = useState(0); - const [loading, setLoading] = useState(false); - let amountEffective: Amounts.AmountJson | undefined = undefined; - - useEffect(() => { - const doFetch = async (): Promise<void> => { - const p = await wxApi.preparePay(talerPayUri); - setPayStatus(p); - }; - doFetch(); - }, [numTries, talerPayUri]); - - if (!payStatus) { - return <span>Loading payment information ...</span>; - } - - let insufficientBalance = false; - if (payStatus.status == "insufficient-balance") { - insufficientBalance = true; - } - - if (payStatus.status === "payment-possible") { - amountEffective = Amounts.parseOrThrow(payStatus.amountEffective); - } - - if (payStatus.status === PreparePayResultType.AlreadyConfirmed && numTries === 0) { - return ( - <span> - You have already paid for this article. Click{" "} - <a href={payStatus.nextUrl}>here</a> to view it again. - </span> - ); - } - - let contractTerms: ContractTerms; - - try { - contractTerms = codecForContractTerms().decode(payStatus.contractTerms); - } catch (e) { - // This should never happen, as the wallet is supposed to check the contract terms - // before storing them. - console.error(e); - console.log("raw contract terms were", payStatus.contractTerms); - return <span>Invalid contract terms.</span>; - } - - if (!contractTerms) { - return ( - <span> - Error: did not get contract terms from merchant or wallet backend. - </span> - ); - } - - let merchantName: React.ReactElement; - if (contractTerms.merchant && contractTerms.merchant.name) { - merchantName = <strong>{contractTerms.merchant.name}</strong>; - } else { - merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>; - } - - const amount = ( - <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong> - ); - - const doPayment = async (): Promise<void> => { - if (payStatus.status !== "payment-possible") { - throw Error(`invalid state: ${payStatus.status}`); - } - const proposalId = payStatus.proposalId; - setNumTries(numTries + 1); - try { - setLoading(true); - const res = await wxApi.confirmPay(proposalId, undefined); - document.location.href = res.nextUrl; - } catch (e) { - console.error(e); - setPayErrMsg(e.message); - } - }; - - return ( - <div> - <p> - <i18n.Translate wrap="p"> - The merchant <span>{merchantName}</span> offers you to purchase: - </i18n.Translate> - <div style={{ textAlign: "center" }}> - <strong>{contractTerms.summary}</strong> - </div> - {amountEffective ? ( - <i18n.Translate wrap="p"> - The total price is <span>{amount} </span> - (plus <span>{renderAmount(amountEffective)}</span> fees). - </i18n.Translate> - ) : ( - <i18n.Translate wrap="p"> - The total price is <span>{amount}</span>. - </i18n.Translate> - )} - </p> - - {insufficientBalance ? ( - <div> - <p style={{ color: "red", fontWeight: "bold" }}> - Unable to pay: Your balance is insufficient. - </p> - </div> - ) : null} - - {payErrMsg ? ( - <div> - <p>Payment failed: {payErrMsg}</p> - <button - className="pure-button button-success" - onClick={() => doPayment()} - > - {i18n.str`Retry`} - </button> - </div> - ) : ( - <div> - <ProgressButton - loading={loading} - disabled={insufficientBalance} - onClick={() => doPayment()} - > - {i18n.str`Confirm payment`} - </ProgressButton> - </div> - )} - </div> - ); -} - -export function createPayPage(): JSX.Element { - const url = new URL(document.location.href); - const talerPayUri = url.searchParams.get("talerPayUri"); - if (!talerPayUri) { - throw Error("invalid parameter"); - } - return <TalerPayDialog talerPayUri={talerPayUri} />; -} diff --git a/src/webex/pages/payback.tsx b/src/webex/pages/payback.tsx deleted file mode 100644 index 5d42f5f47..000000000 --- a/src/webex/pages/payback.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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/> - */ - -/** - * View and edit auditors. - * - * @author Florian Dold - */ - -/** - * Imports. - */ -import * as React from "react"; - -export function makePaybackPage(): JSX.Element { - return <div>not implemented</div>; -} diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx deleted file mode 100644 index 8a99a6d90..000000000 --- a/src/webex/pages/popup.tsx +++ /dev/null @@ -1,499 +0,0 @@ -/* - This file is part of TALER - (C) 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/> - */ - -/** - * Popup shown to the user when they click - * the Taler browser action button. - * - * @author Florian Dold - */ - -/** - * Imports. - */ -import * as i18n from "../i18n"; - -import { AmountJson } from "../../util/amounts"; -import * as Amounts from "../../util/amounts"; - -import { abbrev, renderAmount, PageLink } from "../renderHtml"; -import * as wxApi from "../wxApi"; - -import React, { Fragment, useState, useEffect } from "react"; - -import moment from "moment"; -import { Timestamp } from "../../util/time"; -import { classifyTalerUri, TalerUriType } from "../../util/taleruri"; -import { PermissionsCheckbox } from "./welcome"; -import { BalancesResponse, Balance } from "../../types/walletTypes"; - -// FIXME: move to newer react functions -/* eslint-disable react/no-deprecated */ - -class Router extends React.Component<any, any> { - static setRoute(s: string): void { - window.location.hash = s; - } - - static getRoute(): string { - // Omit the '#' at the beginning - return window.location.hash.substring(1); - } - - static onRoute(f: any): () => void { - Router.routeHandlers.push(f); - return () => { - const i = Router.routeHandlers.indexOf(f); - this.routeHandlers = this.routeHandlers.splice(i, 1); - }; - } - - private static routeHandlers: any[] = []; - - componentWillMount(): void { - console.log("router mounted"); - window.onhashchange = () => { - this.setState({}); - for (const f of Router.routeHandlers) { - f(); - } - }; - } - - render(): JSX.Element { - const route = window.location.hash.substring(1); - console.log("rendering route", route); - let defaultChild: React.ReactChild | null = null; - let foundChild: React.ReactChild | null = null; - React.Children.forEach(this.props.children, (child) => { - const childProps: any = (child as any).props; - if (!childProps) { - return; - } - if (childProps.default) { - defaultChild = child as React.ReactChild; - } - if (childProps.route === route) { - foundChild = child as React.ReactChild; - } - }); - const c: React.ReactChild | null = foundChild || defaultChild; - if (!c) { - throw Error("unknown route"); - } - Router.setRoute((c as any).props.route); - return <div>{c}</div>; - } -} - -interface TabProps { - target: string; - children?: React.ReactNode; -} - -function Tab(props: TabProps): JSX.Element { - let cssClass = ""; - if (props.target === Router.getRoute()) { - cssClass = "active"; - } - const onClick = (e: React.MouseEvent<HTMLAnchorElement>): void => { - Router.setRoute(props.target); - e.preventDefault(); - }; - return ( - <a onClick={onClick} href={props.target} className={cssClass}> - {props.children} - </a> - ); -} - -class WalletNavBar extends React.Component<any, any> { - private cancelSubscription: any; - - componentWillMount(): void { - this.cancelSubscription = Router.onRoute(() => { - this.setState({}); - }); - } - - componentWillUnmount(): void { - if (this.cancelSubscription) { - this.cancelSubscription(); - } - } - - render(): JSX.Element { - console.log("rendering nav bar"); - return ( - <div className="nav" id="header"> - <Tab target="/balance">{i18n.str`Balance`}</Tab> - <Tab target="/history">{i18n.str`History`}</Tab> - <Tab target="/settings">{i18n.str`Settings`}</Tab> - <Tab target="/debug">{i18n.str`Debug`}</Tab> - </div> - ); - } -} - -/** - * Render an amount as a large number with a small currency symbol. - */ -function bigAmount(amount: AmountJson): JSX.Element { - const v = amount.value + amount.fraction / Amounts.fractionalBase; - return ( - <span> - <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "} - <span>{amount.currency}</span> - </span> - ); -} - -function EmptyBalanceView(): JSX.Element { - return ( - <i18n.Translate wrap="p"> - You have no balance to show. Need some{" "} - <PageLink pageName="welcome.html">help</PageLink> getting started? - </i18n.Translate> - ); -} - -class WalletBalanceView extends React.Component<any, any> { - private balance: BalancesResponse; - private gotError = false; - private canceler: (() => void) | undefined = undefined; - private unmount = false; - private updateBalanceRunning = false; - - componentWillMount(): void { - this.canceler = wxApi.onUpdateNotification(() => this.updateBalance()); - this.updateBalance(); - } - - componentWillUnmount(): void { - console.log("component WalletBalanceView will unmount"); - if (this.canceler) { - this.canceler(); - } - this.unmount = true; - } - - async updateBalance(): Promise<void> { - if (this.updateBalanceRunning) { - return; - } - this.updateBalanceRunning = true; - let balance: BalancesResponse; - try { - balance = await wxApi.getBalance(); - } catch (e) { - if (this.unmount) { - return; - } - this.gotError = true; - console.error("could not retrieve balances", e); - this.setState({}); - return; - } finally { - this.updateBalanceRunning = false; - } - if (this.unmount) { - return; - } - this.gotError = false; - console.log("got balance", balance); - this.balance = balance; - this.setState({}); - } - - formatPending(entry: Balance): JSX.Element { - let incoming: JSX.Element | undefined; - let payment: JSX.Element | undefined; - - const available = Amounts.parseOrThrow(entry.available); - const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); - const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); - - console.log( - "available: ", - entry.pendingIncoming ? renderAmount(entry.available) : null, - ); - console.log( - "incoming: ", - entry.pendingIncoming ? renderAmount(entry.pendingIncoming) : null, - ); - - if (Amounts.isNonZero(pendingIncoming)) { - incoming = ( - <i18n.Translate wrap="span"> - <span style={{ color: "darkgreen" }}> - {"+"} - {renderAmount(entry.pendingIncoming)} - </span>{" "} - incoming - </i18n.Translate> - ); - } - - 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> - ); - } - - render(): JSX.Element { - const wallet = this.balance; - if (this.gotError) { - return ( - <div className="balance"> - <p>{i18n.str`Error: could not retrieve balance information.`}</p> - <p> - Click <PageLink pageName="welcome.html">here</PageLink> for help and - diagnostics. - </p> - </div> - ); - } - if (!wallet) { - return <span></span>; - } - console.log(wallet); - const listing = wallet.balances.map((entry) => { - const av = Amounts.parseOrThrow(entry.available); - return ( - <p key={av.currency}> - {bigAmount(av)} {this.formatPending(entry)} - </p> - ); - }); - return listing.length > 0 ? ( - <div className="balance">{listing}</div> - ) : ( - <EmptyBalanceView /> - ); - } -} - -function Icon({ l }: { l: string }): JSX.Element { - return <div className={"icon"}>{l}</div>; -} - -function formatAndCapitalize(text: string): string { - text = text.replace("-", " "); - text = text.replace(/^./, text[0].toUpperCase()); - return text; -} - -const HistoryComponent = (props: any): JSX.Element => { - return <span>TBD</span>; -}; - -class WalletSettings extends React.Component<any, any> { - render(): JSX.Element { - return ( - <div> - <h2>Permissions</h2> - <PermissionsCheckbox /> - </div> - ); - } -} - -function reload(): void { - try { - chrome.runtime.reload(); - window.close(); - } catch (e) { - // Functionality missing in firefox, ignore! - } -} - -function confirmReset(): void { - if ( - confirm( - "Do you want to IRREVOCABLY DESTROY everything inside your" + - " wallet and LOSE ALL YOUR COINS?", - ) - ) { - wxApi.resetDb(); - window.close(); - } -} - -function WalletDebug(props: any): JSX.Element { - return ( - <div> - <p>Debug tools:</p> - <button onClick={openExtensionPage("/popup.html")}>wallet tab</button> - <button onClick={openExtensionPage("/benchmark.html")}>benchmark</button> - <button onClick={openExtensionPage("/show-db.html")}>show db</button> - <button onClick={openExtensionPage("/tree.html")}>show tree</button> - <br /> - <button onClick={confirmReset}>reset</button> - <button onClick={reload}>reload chrome extension</button> - </div> - ); -} - -function openExtensionPage(page: string) { - return () => { - chrome.tabs.create({ - url: chrome.extension.getURL(page), - }); - }; -} - -function openTab(page: string) { - return (evt: React.SyntheticEvent<any>) => { - evt.preventDefault(); - chrome.tabs.create({ - url: page, - }); - }; -} - -function makeExtensionUrlWithParams( - url: string, - params?: { [name: string]: string | undefined }, -): string { - const innerUrl = new URL(chrome.extension.getURL("/" + url)); - if (params) { - for (const key in params) { - const p = params[key]; - if (p) { - innerUrl.searchParams.set(key, p); - } - } - } - return innerUrl.href; -} - -function actionForTalerUri(talerUri: string): string | undefined { - const uriType = classifyTalerUri(talerUri); - switch (uriType) { - case TalerUriType.TalerWithdraw: - return makeExtensionUrlWithParams("withdraw.html", { - talerWithdrawUri: talerUri, - }); - case TalerUriType.TalerPay: - return makeExtensionUrlWithParams("pay.html", { - talerPayUri: talerUri, - }); - case TalerUriType.TalerTip: - return makeExtensionUrlWithParams("tip.html", { - talerTipUri: talerUri, - }); - case TalerUriType.TalerRefund: - return makeExtensionUrlWithParams("refund.html", { - talerRefundUri: talerUri, - }); - case TalerUriType.TalerNotifyReserve: - // FIXME: implement - break; - default: - console.warn( - "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.", - ); - break; - } - return undefined; -} - -async function findTalerUriInActiveTab(): Promise<string | undefined> { - return new Promise((resolve, reject) => { - chrome.tabs.executeScript( - { - code: ` - (() => { - let x = document.querySelector("a[href^='taler://'"); - return x ? x.href.toString() : null; - })(); - `, - allFrames: false, - }, - (result) => { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError); - resolve(undefined); - return; - } - console.log("got result", result); - resolve(result[0]); - }, - ); - }); -} - -function WalletPopup(): JSX.Element { - const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>( - undefined, - ); - const [dismissed, setDismissed] = useState(false); - useEffect(() => { - async function check(): Promise<void> { - const talerUri = await findTalerUriInActiveTab(); - if (talerUri) { - const actionUrl = actionForTalerUri(talerUri); - setTalerActionUrl(actionUrl); - } - } - check(); - }); - if (talerActionUrl && !dismissed) { - return ( - <div style={{ padding: "1em" }}> - <h1>Taler Action</h1> - <p>This page has a Taler action. </p> - <p> - <button - onClick={() => { - window.open(talerActionUrl, "_blank"); - }} - > - Open - </button> - </p> - <p> - <button onClick={() => setDismissed(true)}>Dismiss</button> - </p> - </div> - ); - } - return ( - <div> - <WalletNavBar /> - <div style={{ margin: "1em" }}> - <Router> - <WalletBalanceView route="/balance" default /> - <WalletSettings route="/settings" /> - <WalletDebug route="/debug" /> - </Router> - </div> - </div> - ); -} - -export function createPopup(): JSX.Element { - return <WalletPopup />; -} diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx deleted file mode 100644 index c5d6a00df..000000000 --- a/src/webex/pages/refund.tsx +++ /dev/null @@ -1,89 +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 that shows refund status for purchases. - * - * @author Florian Dold - */ - -import React, { useEffect, useState } from "react"; - -import * as wxApi from "../wxApi"; -import { PurchaseDetails } from "../../types/walletTypes"; -import { AmountView } from "../renderHtml"; - -function RefundStatusView(props: { talerRefundUri: string }): JSX.Element { - const [applied, setApplied] = useState(false); - const [purchaseDetails, setPurchaseDetails] = useState< - PurchaseDetails | undefined - >(undefined); - const [errMsg, setErrMsg] = useState<string | undefined>(undefined); - - useEffect(() => { - const doFetch = async (): Promise<void> => { - try { - const result = await wxApi.applyRefund(props.talerRefundUri); - setApplied(true); - const r = await wxApi.getPurchaseDetails(result.proposalId); - setPurchaseDetails(r); - } catch (e) { - console.error(e); - setErrMsg(e.message); - console.log("err message", e.message); - } - }; - doFetch(); - }, [props.talerRefundUri]); - - console.log("rendering"); - - if (errMsg) { - return <span>Error: {errMsg}</span>; - } - - if (!applied || !purchaseDetails) { - return <span>Updating refund status</span>; - } - - return ( - <> - <h2>Refund Status</h2> - <p> - The product <em>{purchaseDetails.contractTerms.summary}</em> has - received a total refund of{" "} - <AmountView amount={purchaseDetails.totalRefundAmount} />. - </p> - <p>Note that additional fees from the exchange may apply.</p> - </> - ); -} - -export function createRefundPage(): JSX.Element { - const url = new URL(document.location.href); - - const container = document.getElementById("container"); - if (!container) { - throw Error("fatal: can't mount component, container missing"); - } - - const talerRefundUri = url.searchParams.get("talerRefundUri"); - if (!talerRefundUri) { - throw Error("taler refund URI requred"); - } - - return <RefundStatusView talerRefundUri={talerRefundUri} />; -} diff --git a/src/webex/pages/reset-required.tsx b/src/webex/pages/reset-required.tsx deleted file mode 100644 index 9e40e7981..000000000 --- a/src/webex/pages/reset-required.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - This file is part of TALER - (C) 2017 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 to inform the user when a database reset is required. - * - * @author Florian Dold - */ - -import * as React from "react"; - -import * as wxApi from "../wxApi"; - -class State { - /** - * Did the user check the confirmation check box? - */ - checked: boolean; - - /** - * Do we actually need to reset the db? - */ - resetRequired: boolean; -} - -class ResetNotification extends React.Component<any, State> { - constructor(props: any) { - super(props); - this.state = { checked: false, resetRequired: true }; - setInterval(() => this.update(), 500); - } - async update(): Promise<void> { - const res = await wxApi.checkUpgrade(); - this.setState({ resetRequired: res.dbResetRequired }); - } - render(): JSX.Element { - if (this.state.resetRequired) { - return ( - <div> - <h1>Manual Reset Reqired</h1> - <p> - The wallet's database in your browser is incompatible with the{" "} - currently installed wallet. Please reset manually. - </p> - <p> - Once the database format has stabilized, we will provide automatic - upgrades. - </p> - <input - id="check" - type="checkbox" - checked={this.state.checked} - onChange={(e) => this.setState({ checked: e.target.checked })} - />{" "} - <label htmlFor="check"> - I understand that I will lose all my data - </label> - <br /> - <button - className="pure-button" - disabled={!this.state.checked} - onClick={() => wxApi.resetDb()} - > - Reset - </button> - </div> - ); - } - return ( - <div> - <h1>Everything is fine!</h1>A reset is not required anymore, you can - close this page. - </div> - ); - } -} - -export function createResetRequiredPage(): JSX.Element { - return <ResetNotification />; -} diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx deleted file mode 100644 index e8cf8c9dd..000000000 --- a/src/webex/pages/return-coins.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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/> - */ - -/** - * Return coins to own bank account. - * - * @author Florian Dold - */ - -/** - * Imports. - */ -import * as React from "react"; - -export function createReturnCoinsPage(): JSX.Element { - return <span>Not implemented yet.</span>; -} diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx deleted file mode 100644 index 4a1d3743a..000000000 --- a/src/webex/pages/tip.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - This file is part of TALER - (C) 2017 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 * as React from "react"; - -import { acceptTip, getTipStatus } from "../wxApi"; - -import { renderAmount, ProgressButton } from "../renderHtml"; - -import { useState, useEffect } from "react"; -import { TipStatus } from "../../types/walletTypes"; - -function TipDisplay(props: { talerTipUri: string }): JSX.Element { - const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined); - const [discarded, setDiscarded] = useState(false); - const [loading, setLoading] = useState(false); - const [finished, setFinished] = useState(false); - - useEffect(() => { - const doFetch = async (): Promise<void> => { - const ts = await getTipStatus(props.talerTipUri); - setTipStatus(ts); - }; - doFetch(); - }, [props.talerTipUri]); - - if (discarded) { - return <span>You've discarded the tip.</span>; - } - - if (finished) { - return <span>Tip has been accepted!</span>; - } - - if (!tipStatus) { - return <span>Loading ...</span>; - } - - const discard = (): void => { - setDiscarded(true); - }; - - const accept = async (): Promise<void> => { - setLoading(true); - await acceptTip(tipStatus.tipId); - setFinished(true); - }; - - return ( - <div> - <h2>Tip Received!</h2> - <p> - You received a tip of <strong>{renderAmount(tipStatus.amount)}</strong>{" "} - from <span> </span> - <strong>{tipStatus.merchantOrigin}</strong>. - </p> - <p> - The tip is handled by the exchange{" "} - <strong>{tipStatus.exchangeUrl}</strong>. This exchange will charge fees - of <strong>{renderAmount(tipStatus.totalFees)}</strong> for this - operation. - </p> - <form className="pure-form"> - <ProgressButton loading={loading} onClick={() => accept()}> - Accept Tip - </ProgressButton>{" "} - <button className="pure-button" type="button" onClick={() => discard()}> - Discard tip - </button> - </form> - </div> - ); -} - -export function createTipPage(): JSX.Element { - const url = new URL(document.location.href); - const talerTipUri = url.searchParams.get("talerTipUri"); - if (typeof talerTipUri !== "string") { - throw Error("talerTipUri must be a string"); - } - - return <TipDisplay talerTipUri={talerTipUri} />; -} diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx deleted file mode 100644 index a7c24d659..000000000 --- a/src/webex/pages/welcome.tsx +++ /dev/null @@ -1,190 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 Taler Systems SA - - 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/> - */ - -/** - * Welcome page, shown on first installs. - * - * @author Florian Dold - */ - -import React, { useState, useEffect } from "react"; -import { getDiagnostics } from "../wxApi"; -import { PageLink } from "../renderHtml"; -import { WalletDiagnostics } from "../../types/walletTypes"; -import * as wxApi from "../wxApi"; -import { getPermissionsApi } from "../compat"; -import { extendedPermissions } from "../permissions"; - -function Diagnostics(): JSX.Element | null { - const [timedOut, setTimedOut] = useState(false); - const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>( - undefined, - ); - - useEffect(() => { - let gotDiagnostics = false; - setTimeout(() => { - if (!gotDiagnostics) { - console.error("timed out"); - setTimedOut(true); - } - }, 1000); - const doFetch = async (): Promise<void> => { - const d = await getDiagnostics(); - console.log("got diagnostics", d); - gotDiagnostics = true; - setDiagnostics(d); - }; - console.log("fetching diagnostics"); - doFetch(); - }, []); - - if (timedOut) { - return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>; - } - - if (diagnostics) { - if (diagnostics.errors.length === 0) { - return null; - } else { - return ( - <div - style={{ - borderLeft: "0.5em solid red", - paddingLeft: "1em", - paddingTop: "0.2em", - paddingBottom: "0.2em", - }} - > - <p>Problems detected:</p> - <ol> - {diagnostics.errors.map((errMsg) => ( - <li key={errMsg}>{errMsg}</li> - ))} - </ol> - {diagnostics.firefoxIdbProblem ? ( - <p> - Please check in your <code>about:config</code> settings that you - have IndexedDB enabled (check the preference name{" "} - <code>dom.indexedDB.enabled</code>). - </p> - ) : null} - {diagnostics.dbOutdated ? ( - <p> - Your wallet database is outdated. Currently automatic migration is - not supported. Please go{" "} - <PageLink pageName="reset-required.html">here</PageLink> to reset - the wallet database. - </p> - ) : null} - </div> - ); - } - } - - return <p>Running diagnostics ...</p>; -} - -export function PermissionsCheckbox(): JSX.Element { - const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState( - false, - ); - async function handleExtendedPerm(requestedVal: boolean): Promise<void> { - let nextVal: boolean | undefined; - if (requestedVal) { - const granted = await new Promise<boolean>((resolve, reject) => { - // We set permissions here, since apparently FF wants this to be done - // as the result of an input event ... - getPermissionsApi().request(extendedPermissions, (granted: boolean) => { - if (chrome.runtime.lastError) { - console.error("error requesting permissions"); - console.error(chrome.runtime.lastError); - reject(chrome.runtime.lastError); - return; - } - console.log("permissions granted:", granted); - resolve(granted); - }); - }); - const res = await wxApi.setExtendedPermissions(granted); - console.log(res); - nextVal = res.newValue; - } else { - const res = await wxApi.setExtendedPermissions(false); - console.log(res); - nextVal = res.newValue; - } - console.log("new permissions applied:", nextVal); - setExtendedPermissionsEnabled(nextVal ?? false); - } - useEffect(() => { - async function getExtendedPermValue(): Promise<void> { - const res = await wxApi.getExtendedPermissions(); - setExtendedPermissionsEnabled(res.newValue); - } - getExtendedPermValue(); - }); - return ( - <div> - <input - checked={extendedPermissionsEnabled} - onChange={(x) => handleExtendedPerm(x.target.checked)} - type="checkbox" - id="checkbox-perm" - style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} - /> - <label - htmlFor="checkbox-perm" - style={{ marginLeft: "0.5em", fontWeight: "bold" }} - > - Automatically open wallet based on page content - </label> - <span - style={{ - color: "#383838", - fontSize: "smaller", - display: "block", - marginLeft: "2em", - }} - > - (Enabling this option below will make using the wallet faster, but - requires more permissions from your browser.) - </span> - </div> - ); -} - -function Welcome(): JSX.Element { - return ( - <> - <p>Thank you for installing the wallet.</p> - <Diagnostics /> - <h2>Permissions</h2> - <PermissionsCheckbox /> - <h2>Next Steps</h2> - <a href="https://demo.taler.net/" style={{ display: "block" }}> - Try the demo » - </a> - <a href="https://demo.taler.net/" style={{ display: "block" }}> - Learn how to top up your wallet balance » - </a> - </> - ); -} - -export function createWelcomePage(): JSX.Element { - return <Welcome />; -} diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx deleted file mode 100644 index 4a92704b3..000000000 --- a/src/webex/pages/withdraw.tsx +++ /dev/null @@ -1,229 +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 * as i18n from "../i18n"; - -import { WithdrawDetailView, renderAmount } from "../renderHtml"; - -import React, { useState, useEffect } from "react"; -import { - acceptWithdrawal, - onUpdateNotification, -} from "../wxApi"; - -function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element { - const [details, setDetails] = useState< - any | undefined - >(); - const [selectedExchange, setSelectedExchange] = useState< - string | undefined - >(); - const talerWithdrawUri = props.talerWithdrawUri; - const [cancelled, setCancelled] = useState(false); - const [selecting, setSelecting] = useState(false); - const [customUrl, setCustomUrl] = useState<string>(""); - const [errMsg, setErrMsg] = useState<string | undefined>(""); - const [updateCounter, setUpdateCounter] = useState(1); - - useEffect(() => { - return onUpdateNotification(() => { - setUpdateCounter(updateCounter + 1); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - const fetchData = async (): Promise<void> => { - // FIXME: re-implement with new API - // console.log("getting from", talerWithdrawUri); - // let d: WithdrawalDetailsResponse | undefined = undefined; - // try { - // d = await getWithdrawDetails(talerWithdrawUri, selectedExchange); - // } catch (e) { - // console.error( - // `error getting withdraw details for uri ${talerWithdrawUri}, exchange ${selectedExchange}`, - // e, - // ); - // setErrMsg(e.message); - // return; - // } - // console.log("got withdrawDetails", d); - // if (!selectedExchange && d.bankWithdrawDetails.suggestedExchange) { - // console.log("setting selected exchange"); - // setSelectedExchange(d.bankWithdrawDetails.suggestedExchange); - // } - // setDetails(d); - }; - fetchData(); - }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]); - - if (errMsg) { - return ( - <div> - <i18n.Translate wrap="p"> - Could not get details for withdraw operation: - </i18n.Translate> - <p style={{ color: "red" }}>{errMsg}</p> - <p> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => { - setSelecting(true); - setErrMsg(undefined); - setSelectedExchange(undefined); - setDetails(undefined); - }} - > - {i18n.str`Chose different exchange provider`} - </span> - </p> - </div> - ); - } - - if (!details) { - return <span>Loading...</span>; - } - - if (cancelled) { - return <span>Withdraw operation has been cancelled.</span>; - } - - if (selecting) { - const bankSuggestion = - details && details.bankWithdrawDetails.suggestedExchange; - return ( - <div> - {i18n.str`Please select an exchange. You can review the details before after your selection.`} - {bankSuggestion && ( - <div> - <h2>Bank Suggestion</h2> - <button - className="pure-button button-success" - onClick={() => { - setDetails(undefined); - setSelectedExchange(bankSuggestion); - setSelecting(false); - }} - > - <i18n.Translate wrap="span"> - Select <strong>{bankSuggestion}</strong> - </i18n.Translate> - </button> - </div> - )} - <h2>Custom Selection</h2> - <p> - <input - type="text" - onChange={(e) => setCustomUrl(e.target.value)} - value={customUrl} - /> - </p> - <button - className="pure-button button-success" - onClick={() => { - setDetails(undefined); - setSelectedExchange(customUrl); - setSelecting(false); - }} - > - <i18n.Translate wrap="span">Select custom exchange</i18n.Translate> - </button> - </div> - ); - } - - const accept = async (): Promise<void> => { - if (!selectedExchange) { - throw Error("can't accept, no exchange selected"); - } - console.log("accepting exchange", selectedExchange); - const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange); - console.log("accept withdrawal response", res); - if (res.confirmTransferUrl) { - document.location.href = res.confirmTransferUrl; - } - }; - - return ( - <div> - <h1>Digital Cash Withdrawal</h1> - <i18n.Translate wrap="p"> - You are about to withdraw{" "} - <strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from - your bank account into your wallet. - </i18n.Translate> - {selectedExchange ? ( - <p> - The exchange <strong>{selectedExchange}</strong> will be used as the - Taler payment service provider. - </p> - ) : null} - - <div> - <button - className="pure-button button-success" - disabled={!selectedExchange} - onClick={() => accept()} - > - {i18n.str`Accept fees and withdraw`} - </button> - <p> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setSelecting(true)} - > - {i18n.str`Chose different exchange provider`} - </span> - <br /> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setCancelled(true)} - > - {i18n.str`Cancel withdraw operation`} - </span> - </p> - - {details.exchangeWithdrawDetails ? ( - <WithdrawDetailView rci={details.exchangeWithdrawDetails} /> - ) : null} - </div> - </div> - ); -} - -export function createWithdrawPage(): JSX.Element { - const url = new URL(document.location.href); - const talerWithdrawUri = url.searchParams.get("talerWithdrawUri"); - if (!talerWithdrawUri) { - throw Error("withdraw URI required"); - } - return <WithdrawalDialog talerWithdrawUri={talerWithdrawUri} />; -} |