diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-11-13 23:30:18 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-11-13 23:31:17 +0100 |
commit | f3fb8be7db6de87dae40d41bd5597a735c800ca1 (patch) | |
tree | 1a061db04de8f5bb5a6b697fa56a9948f67fac2f /pages | |
parent | 200d83c3886149ebb3f018530302079e12a81f6b (diff) | |
download | wallet-core-f3fb8be7db6de87dae40d41bd5597a735c800ca1.tar.xz |
restructuring
Diffstat (limited to 'pages')
-rw-r--r-- | pages/confirm-contract.html | 75 | ||||
-rw-r--r-- | pages/confirm-contract.tsx | 234 | ||||
-rw-r--r-- | pages/confirm-create-reserve.html | 93 | ||||
-rw-r--r-- | pages/confirm-create-reserve.tsx | 395 | ||||
-rw-r--r-- | pages/debug.html | 13 | ||||
-rw-r--r-- | pages/help/empty-wallet.html | 30 | ||||
-rw-r--r-- | pages/show-db.html | 15 | ||||
-rw-r--r-- | pages/show-db.ts | 57 | ||||
-rw-r--r-- | pages/tree.html | 36 | ||||
-rw-r--r-- | pages/tree.tsx | 400 |
10 files changed, 0 insertions, 1348 deletions
diff --git a/pages/confirm-contract.html b/pages/confirm-contract.html deleted file mode 100644 index 5c6c9a446..000000000 --- a/pages/confirm-contract.html +++ /dev/null @@ -1,75 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <title>Taler Wallet: Confirm Reserve Creation</title> - - <link rel="stylesheet" type="text/css" href="../style/lang.css"> - <link rel="stylesheet" type="text/css" href="../style/wallet.css"> - - <link rel="icon" href="../img/icon.png"> - - <script src="../lib/vendor/URI.js"></script> - <script src="../lib/vendor/react.js"></script> - <script src="../lib/vendor/react-dom.js"></script> - <script src="../lib/vendor/system-csp-production.src.js"></script> - <!-- <script src="../lib/vendor/jed.js"></script> --> - <script src="../lib/i18n.js"></script> - <script src="../i18n/strings.js"></script> - <script src="../lib/module-trampoline.js"></script> - - <style> - button.accept { - background-color: #5757D2; - border: 1px solid black; - border-radius: 5px; - margin: 1em 0; - padding: 0.5em; - font-weight: bold; - color: white; - } - button.linky { - background:none!important; - border:none; - padding:0!important; - - font-family:arial,sans-serif; - color:#069; - text-decoration:underline; - cursor:pointer; - } - - input.url { - width: 25em; - } - - - button.accept:disabled { - background-color: #dedbe8; - border: 1px solid white; - border-radius: 5px; - margin: 1em 0; - padding: 0.5em; - font-weight: bold; - color: #2C2C2C; - } - - .errorbox { - border: 1px solid; - display: inline-block; - margin: 1em; - padding: 1em; - font-weight: bold; - background: #FF8A8A; - } - </style> -</head> - -<body> - <section id="main"> - <h1>GNU Taler Wallet</h1> - <article id="contract" class="fade"></article> - </section> -</body> - -</html> diff --git a/pages/confirm-contract.tsx b/pages/confirm-contract.tsx deleted file mode 100644 index b5ada6045..000000000 --- a/pages/confirm-contract.tsx +++ /dev/null @@ -1,234 +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. - * - * @author Florian Dold - */ - - -import {substituteFulfillmentUrl} from "../lib/wallet/helpers"; -import {Contract, AmountJson, IExchangeInfo} from "../lib/wallet/types"; -import {Offer} from "../lib/wallet/wallet"; -import {renderContract, prettyAmount} from "../lib/wallet/renderHtml"; -"use strict"; -import {getExchanges} from "../lib/wallet/wxApi"; - - -interface DetailState { - collapsed: boolean; - exchanges: null|IExchangeInfo[]; -} - -interface DetailProps { - contract: Contract - collapsed: boolean -} - - -class Details extends React.Component<DetailProps, DetailState> { - constructor(props: DetailProps) { - super(props); - console.log("new Details component created"); - this.state = { - collapsed: props.collapsed, - exchanges: null - }; - - console.log("initial state:", this.state); - - this.update(); - } - - async update() { - let exchanges = await getExchanges(); - this.setState({exchanges} as any); - } - - render() { - console.log("details collapsed (state)", this.state.collapsed); - console.log("details collapsed (prop)", this.props.collapsed); - if (this.state.collapsed) { - return ( - <div> - <button className="linky" - onClick={() => { this.setState({collapsed: false} as any)}}> - show more details - </button> - </div> - ); - } else { - return ( - <div> - <button className="linky" - onClick={() => this.setState({collapsed: true} as any)}> - show less details - </button> - <div> - Accepted exchanges: - <ul> - {this.props.contract.exchanges.map( - e => <li>{`${e.url}: ${e.master_pub}`}</li>)} - </ul> - Exchanges in the wallet: - <ul> - {(this.state.exchanges || []).map( - (e: IExchangeInfo) => - <li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)} - </ul> - </div> - </div>); - } - } -} - -interface ContractPromptProps { - offerId: number; -} - -interface ContractPromptState { - offer: any; - error: string|null; - payDisabled: boolean; -} - -class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> { - constructor() { - super(); - this.state = { - offer: undefined, - error: null, - payDisabled: true, - } - } - - componentWillMount() { - this.update(); - this.checkPayment(); - } - - componentWillUnmount() { - // FIXME: abort running ops - } - - async update() { - let offer = await this.getOffer(); - this.setState({offer} as any); - this.checkPayment(); - } - - getOffer(): Promise<Offer> { - return new Promise((resolve, reject) => { - let msg = { - type: 'get-offer', - detail: { - offerId: this.props.offerId - } - }; - chrome.runtime.sendMessage(msg, (resp) => { - resolve(resp); - }); - }) - } - - checkPayment() { - let msg = { - type: 'check-pay', - detail: { - offer: this.state.offer - } - }; - chrome.runtime.sendMessage(msg, (resp) => { - if (resp.error) { - console.log("check-pay error", JSON.stringify(resp)); - switch (resp.error) { - case "coins-insufficient": - this.state.error = i18n`You have insufficient funds of the requested currency in your wallet.`; - break; - default: - this.state.error = `Error: ${resp.error}`; - break; - } - this.state.payDisabled = true; - } else { - this.state.payDisabled = false; - this.state.error = null; - } - this.setState({} as any); - window.setTimeout(() => this.checkPayment(), 500); - }); - } - - doPayment() { - let d = {offer: this.state.offer}; - chrome.runtime.sendMessage({type: 'confirm-pay', detail: d}, (resp) => { - if (resp.error) { - console.log("confirm-pay error", JSON.stringify(resp)); - switch (resp.error) { - case "coins-insufficient": - this.state.error = "You do not have enough coins of the" + - " requested currency."; - break; - default: - this.state.error = `Error: ${resp.error}`; - break; - } - this.setState({} as any); - return; - } - let c = d.offer.contract; - console.log("contract", c); - document.location.href = substituteFulfillmentUrl(c.fulfillment_url, - this.state.offer); - }); - } - - - render() { - if (!this.state.offer) { - return <span>...</span>; - } - let c = this.state.offer.contract; - return ( - <div> - <div> - {renderContract(c)} - </div> - <button onClick={() => this.doPayment()} - disabled={this.state.payDisabled} - className="accept"> - Confirm payment - </button> - <div> - {(this.state.error ? <p className="errorbox">{this.state.error}</p> : <p />)} - </div> - <Details contract={c} collapsed={!this.state.error}/> - </div> - ); - } -} - - -export function main() { - let url = URI(document.location.href); - let query: any = URI.parseQuery(url.query()); - let offerId = JSON.parse(query.offerId); - - ReactDOM.render(<ContractPrompt offerId={offerId}/>, document.getElementById( - "contract")!); -} diff --git a/pages/confirm-create-reserve.html b/pages/confirm-create-reserve.html deleted file mode 100644 index c31a4aa8a..000000000 --- a/pages/confirm-create-reserve.html +++ /dev/null @@ -1,93 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <title>Taler Wallet: Select Taler Provider</title> - - <link rel="icon" href="../img/icon.png"> - - <script src="../lib/vendor/URI.js"></script> - <script src="../lib/vendor/react.js"></script> - <script src="../lib/vendor/react-dom.js"></script> - - <!-- i18n --> - <script src="../lib/vendor/jed.js"></script> - <script src="../lib/i18n.js"></script> - <script src="../i18n/strings.js"></script> - - <!-- module loading --> - <script src="../lib/vendor/system-csp-production.src.js"></script> - <script src="../lib/module-trampoline.js"></script> - - - <style> - #main { - border: solid 1px black; - border-radius: 10px; - margin: auto; - max-width: 50%; - padding: 2em; - } - - button.accept { - background-color: #5757D2; - border: 1px solid black; - border-radius: 5px; - margin: 1em 0; - padding: 0.5em; - font-weight: bold; - color: white; - } - button.linky { - background:none!important; - border:none; - padding:0!important; - - font-family:arial,sans-serif; - color:#069; - text-decoration:underline; - cursor:pointer; - } - - - button.accept:disabled { - background-color: #dedbe8; - border: 1px solid white; - border-radius: 5px; - margin: 1em 0; - padding: 0.5em; - font-weight: bold; - color: #2C2C2C; - } - - input.url { - width: 25em; - } - - table { - border-collapse: collapse; - } - - td { - border-left: 1px solid black; - border-right: 1px solid black; - text-align: center; - padding: 0.3em; - } - - span.spacer { - padding-left: 0.5em; - padding-right: 0.5em; - } - - </style> -</head> - -<body> - <section id="main"> - <h1>GNU Taler Wallet</h1> - <div class="fade" id="exchange-selection"></div> - </section> -</body> - -</html> diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx deleted file mode 100644 index 833bfed27..000000000 --- a/pages/confirm-create-reserve.tsx +++ /dev/null @@ -1,395 +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 {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers"; -import {AmountJson, CreateReserveResponse} from "../lib/wallet/types"; -import {ReserveCreationInfo, Amounts} from "../lib/wallet/types"; -import {Denomination} from "../lib/wallet/types"; -import {getReserveCreationInfo} from "../lib/wallet/wxApi"; -import {ImplicitStateComponent, StateHolder} from "../lib/components"; - -"use strict"; - - -function delay<T>(delayMs: number, value: T): Promise<T> { - return new Promise<T>((resolve, reject) => { - setTimeout(() => resolve(value), delayMs); - }); -} - -class EventTrigger { - triggerResolve: any; - triggerPromise: Promise<boolean>; - - constructor() { - this.reset(); - } - - private reset() { - this.triggerPromise = new Promise<boolean>((resolve, reject) => { - this.triggerResolve = resolve; - }); - } - - trigger() { - this.triggerResolve(false); - this.reset(); - } - - async wait(delayMs: number): Promise<boolean> { - return await Promise.race([this.triggerPromise, delay(delayMs, true)]); - } -} - - -function renderReserveCreationDetails(rci: ReserveCreationInfo|null) { - if (!rci) { - return <p> - Details will be displayed when a valid exchange provider URL is entered.</p> - } - - let denoms = rci.selectedDenoms; - - let countByPub: {[s: string]: number} = {}; - let uniq: Denomination[] = []; - - denoms.forEach((x: Denomination) => { - let c = countByPub[x.denom_pub] || 0; - if (c == 0) { - uniq.push(x); - } - c += 1; - countByPub[x.denom_pub] = c; - }); - - function row(denom: Denomination) { - return ( - <tr> - <td>{countByPub[denom.denom_pub] + "x"}</td> - <td>{amountToPretty(denom.value)}</td> - <td>{amountToPretty(denom.fee_withdraw)}</td> - <td>{amountToPretty(denom.fee_refresh)}</td> - <td>{amountToPretty(denom.fee_deposit)}</td> - </tr> - ); - } - - let withdrawFeeStr = amountToPretty(rci.withdrawFee); - let overheadStr = amountToPretty(rci.overhead); - - return ( - <div> - <p>{`Withdrawal fees: ${withdrawFeeStr}`}</p> - <p>{`Rounding loss: ${overheadStr}`}</p> - <table> - <thead> - <th># Coins</th> - <th>Value</th> - <th>Withdraw Fee</th> - <th>Refresh Fee</th> - <th>Deposit fee</th> - </thead> - <tbody> - {uniq.map(row)} - </tbody> - </table> - </div> - ); -} - - -function getSuggestedExchange(currency: string): Promise<string> { - // TODO: make this request go to the wallet backend - // Right now, this is a stub. - const defaultExchange: {[s: string]: string} = { - "KUDOS": "https://exchange.demo.taler.net", - "PUDOS": "https://exchange.test.taler.net", - }; - - let exchange = defaultExchange[currency]; - - if (!exchange) { - exchange = "" - } - - return Promise.resolve(exchange); -} - - -function WithdrawFee(props: {reserveCreationInfo: ReserveCreationInfo|null}): JSX.Element { - if (props.reserveCreationInfo) { - let {overhead, withdrawFee} = props.reserveCreationInfo; - let totalCost = Amounts.add(overhead, withdrawFee).amount; - return <p>Withdraw fees: {amountToPretty(totalCost)}</p>; - } - return <p />; -} - - -interface ExchangeSelectionProps { - suggestedExchangeUrl: string; - amount: AmountJson; - callback_url: string; - wt_types: string[]; -} - - -class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> { - statusString: StateHolder<string|null> = this.makeState(null); - reserveCreationInfo: StateHolder<ReserveCreationInfo|null> = this.makeState( - null); - url: StateHolder<string|null> = this.makeState(null); - detailCollapsed: StateHolder<boolean> = this.makeState(true); - - updateEvent = new EventTrigger(); - - constructor(props: ExchangeSelectionProps) { - super(props); - this.onUrlChanged(props.suggestedExchangeUrl || null); - } - - - renderAdvanced(): JSX.Element { - if (this.detailCollapsed() && this.url() !== null && !this.statusString()) { - return ( - <button className="linky" - onClick={() => this.detailCollapsed(false)}> - view fee structure / select different exchange provider - </button> - ); - } - return ( - <div> - <h2>Provider Selection</h2> - <label>URL: </label> - <input className="url" type="text" spellCheck={false} - value={this.url()!} - key="exchange-url-input" - onInput={(e) => this.onUrlChanged((e.target as HTMLInputElement).value)}/> - <br /> - {this.renderStatus()} - <h2>Detailed Fee Structure</h2> - {renderReserveCreationDetails(this.reserveCreationInfo())} - </div>) - } - - renderFee() { - if (!this.reserveCreationInfo()) { - return "??"; - } - let rci = this.reserveCreationInfo()!; - let totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount; - return `${amountToPretty(totalCost)}`; - } - - renderFeeStatus() { - if (this.reserveCreationInfo()) { - return ( - <p> - The exchange provider will charge - {" "} - {this.renderFee()} - {" "} - in fees. - </p> - ); - } - if (this.url() && !this.statusString()) { - let shortName = URI(this.url()!).host(); - return <p> - Waiting for a response from - {" "} - <em>{shortName}</em> - </p>; - } - if (this.statusString()) { - return ( - <p> - <strong style={{color: "red"}}>A problem occured, see below.</strong> - </p> - ); - } - return ( - <p> - Information about fees will be available when an exchange provider is selected. - </p> - ); - } - - render(): JSX.Element { - return ( - <div> - <p> - {"You are about to withdraw "} - <strong>{amountToPretty(this.props.amount)}</strong> - {" from your bank account into your wallet."} - </p> - {this.renderFeeStatus()} - <button className="accept" - disabled={this.reserveCreationInfo() == null} - onClick={() => this.confirmReserve()}> - Accept fees and withdraw - </button> - <br/> - {this.renderAdvanced()} - </div> - ); - } - - - confirmReserve() { - this.confirmReserveImpl(this.reserveCreationInfo()!, - this.url()!, - this.props.amount, - this.props.callback_url); - } - - /** - * Do an update of the reserve creation info, without any debouncing. - */ - async forceReserveUpdate() { - this.reserveCreationInfo(null); - if (!this.url()) { - this.statusString(i18n`Error: URL is empty`); - return; - } - - this.statusString(null); - let parsedUrl = URI(this.url()!); - if (parsedUrl.is("relative")) { - this.statusString(i18n`Error: URL may not be relative`); - return; - } - - try { - let r = await getReserveCreationInfo(this.url()!, - this.props.amount); - console.log("get exchange info resolved"); - this.reserveCreationInfo(r); - console.dir(r); - } catch (e) { - console.log("get exchange info rejected"); - if (e.hasOwnProperty("httpStatus")) { - this.statusString(`Error: request failed with status ${e.httpStatus}`); - } else if (e.hasOwnProperty("errorResponse")) { - let resp = e.errorResponse; - this.statusString(`Error: ${resp.error} (${resp.hint})`); - } - } - } - - reset() { - this.statusString(null); - this.reserveCreationInfo(null); - } - - confirmReserveImpl(rci: ReserveCreationInfo, - exchange: string, - amount: AmountJson, - callback_url: string) { - const d = {exchange, amount}; - const cb = (rawResp: any) => { - if (!rawResp) { - throw Error("empty response"); - } - // FIXME: filter out types that bank/exchange don't have in common - let wire_details = rci.wireInfo; - if (!rawResp.error) { - const resp = CreateReserveResponse.checked(rawResp); - let q: {[name: string]: string|number} = { - wire_details: JSON.stringify(wire_details), - exchange: resp.exchange, - reserve_pub: resp.reservePub, - amount_value: amount.value, - amount_fraction: amount.fraction, - amount_currency: amount.currency, - }; - let url = URI(callback_url).addQuery(q); - if (!url.is("absolute")) { - throw Error("callback url is not absolute"); - } - console.log("going to", url.href()); - document.location.href = url.href(); - } else { - this.reset(); - this.statusString( - `Oops, something went wrong.` + - `The wallet responded with error status (${rawResp.error}).`); - } - }; - chrome.runtime.sendMessage({type: 'create-reserve', detail: d}, cb); - } - - async onUrlChanged(url: string|null) { - this.reset(); - this.url(url); - if (url == undefined) { - return; - } - this.updateEvent.trigger(); - let waited = await this.updateEvent.wait(200); - if (waited) { - // Run the actual update if nobody else preempted us. - this.forceReserveUpdate(); - this.forceUpdate(); - } - } - - renderStatus(): any { - if (this.statusString()) { - return <p><strong style={{color: "red"}}>{this.statusString()}</strong></p>; - } else if (!this.reserveCreationInfo()) { - return <p>Checking URL, please wait ...</p>; - } - return ""; - } -} - -export async function main() { - const url = URI(document.location.href); - const query: any = URI.parseQuery(url.query()); - const amount = AmountJson.checked(JSON.parse(query.amount)); - const callback_url = query.callback_url; - const bank_url = query.bank_url; - const wt_types = JSON.parse(query.wt_types); - - try { - const suggestedExchangeUrl = await getSuggestedExchange(amount.currency); - let args = { - wt_types, - suggestedExchangeUrl, - callback_url, - amount - }; - - ReactDOM.render(<ExchangeSelection {...args} />, document.getElementById( - "exchange-selection")!); - - } catch (e) { - // TODO: provide more context information, maybe factor it out into a - // TODO:generic error reporting function or component. - document.body.innerText = `Fatal error: "${e.message}".`; - console.error(`got error "${e.message}"`, e); - } -} diff --git a/pages/debug.html b/pages/debug.html deleted file mode 100644 index 221c7380c..000000000 --- a/pages/debug.html +++ /dev/null @@ -1,13 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Taler Wallet Debugging</title> - <link rel="icon" href="../img/icon.png"> - </head> - <body> - <h1>Debug Pages</h1> - <a href="show-db.html">Show DB</a> <br> - <a href="../popup/balance-overview.html">Show balance</a> - - </body> -</html> diff --git a/pages/help/empty-wallet.html b/pages/help/empty-wallet.html deleted file mode 100644 index 952bd92b7..000000000 --- a/pages/help/empty-wallet.html +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="utf-8"> - <title>GNU Taler Help - Empty Wallet</title> - <link rel="icon" href="../../img/icon.png"> - <meta name="description" content=""> - <link rel="stylesheet" type="text/css" href="../../style/wallet.css"> - </head> - <body> - <div class="container" id="main"> - <div class="row"> - <div class="col-lg-12"> - <h2 lang="en">Your wallet is empty!</h2> - <p lang="en">You have succeeded with installing the Taler wallet. However, before - you can buy articles using the Taler wallet, you must withdraw electronic coins. - This is typically done by visiting your bank's online banking Web site. There, - you instruct your bank to transfer the funds to a Taler exchange operator. In - return, your wallet will be allowed to withdraw electronic coins.</p> - <p lang="en">At this stage, we are not aware of any regular exchange operators issuing - coins in well-known currencies. However, to see how Taler would work, you - can visit our "fake" bank at - <a href="https://bank.demo.taler.net/">bank.demo.taler.net</a> to - withdraw coins in the "KUDOS" currency that we created just for - demonstrating the system.</p> - </div> - </div> - </div> - </body> -</html> diff --git a/pages/show-db.html b/pages/show-db.html deleted file mode 100644 index 024e844ee..000000000 --- a/pages/show-db.html +++ /dev/null @@ -1,15 +0,0 @@ - -<!doctype html> - -<html> - <head> - <title>Taler Wallet: Reserve Created</title> - <link rel="stylesheet" type="text/css" href="../style/wallet.css"> - <link rel="icon" href="../img/icon.png"> - <script src="show-db.js"></script> - </head> - <body> - <h1>DB Dump</h1> - <pre id="dump"></pre> - </body> -</html> diff --git a/pages/show-db.ts b/pages/show-db.ts deleted file mode 100644 index 71e74388b..000000000 --- a/pages/show-db.ts +++ /dev/null @@ -1,57 +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/> - */ - - -/** - * Wallet database dump for debugging. - * - * @author Florian Dold - */ - -function replacer(match: string, pIndent: string, pKey: string, pVal: string, - pEnd: string) { - var key = '<span class=json-key>'; - var val = '<span class=json-value>'; - var str = '<span class=json-string>'; - var r = pIndent || ''; - if (pKey) { - r = r + key + pKey.replace(/[": ]/g, '') + '</span>: '; - } - if (pVal) { - r = r + (pVal[0] == '"' ? str : val) + pVal + '</span>'; - } - return r + (pEnd || ''); -} - - -function prettyPrint(obj: any) { - var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg; - return JSON.stringify(obj, null as any, 3) - .replace(/&/g, '&').replace(/\\"/g, '"') - .replace(/</g, '<').replace(/>/g, '>') - .replace(jsonLine, replacer); -} - - -document.addEventListener("DOMContentLoaded", () => { - chrome.runtime.sendMessage({type: 'dump-db'}, (resp) => { - const el = document.getElementById('dump'); - if (!el) { - throw Error(); - } - el.innerHTML = prettyPrint(resp); - }); -}); diff --git a/pages/tree.html b/pages/tree.html deleted file mode 100644 index 05022e158..000000000 --- a/pages/tree.html +++ /dev/null @@ -1,36 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <title>Taler Wallet: Tree View</title> - - <link rel="stylesheet" type="text/css" href="../style/lang.css"> - <link rel="stylesheet" type="text/css" href="../style/wallet.css"> - - <link rel="icon" href="../img/icon.png"> - - <script src="../lib/vendor/URI.js"></script> - <script src="../lib/vendor/react.js"></script> - <script src="../lib/vendor/react-dom.js"></script> - - <!-- i18n --> - <script src="../lib/vendor/jed.js"></script> - <script src="../lib/i18n.js"></script> - <script src="../i18n/strings.js"></script> - - <script src="../lib/vendor/system-csp-production.src.js"></script> - <script src="../lib/module-trampoline.js"></script> - - <style> - .tree-item { - margin: 2em; - border-radius: 5px; - border: 1px solid gray; - padding: 1em; - } - </style> - - <body> - <div id="container"></div> - </body> -</html> diff --git a/pages/tree.tsx b/pages/tree.tsx deleted file mode 100644 index 6ff15600f..000000000 --- a/pages/tree.tsx +++ /dev/null @@ -1,400 +0,0 @@ -/* - This file is part of TALER - (C) 2016 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/> - */ - -/** - * Show contents of the wallet as a tree. - * - * @author Florian Dold - */ - - -import { IExchangeInfo } from "../lib/wallet/types"; -import { ReserveRecord, Coin, PreCoin, Denomination } from "../lib/wallet/types"; -import { ImplicitStateComponent, StateHolder } from "../lib/components"; -import { - getReserves, getExchanges, getCoins, getPreCoins, - refresh -} from "../lib/wallet/wxApi"; -import { prettyAmount, abbrev } from "../lib/wallet/renderHtml"; -import { getTalerStampDate } from "../lib/wallet/helpers"; - -interface ReserveViewProps { - reserve: ReserveRecord; -} - -class ReserveView extends React.Component<ReserveViewProps, void> { - render(): JSX.Element { - let r: ReserveRecord = this.props.reserve; - return ( - <div className="tree-item"> - <ul> - <li>Key: {r.reserve_pub}</li> - <li>Created: {(new Date(r.created * 1000).toString())}</li> - <li>Current: {r.current_amount ? prettyAmount(r.current_amount!) : "null"}</li> - <li>Requested: {prettyAmount(r.requested_amount)}</li> - <li>Confirmed: {r.confirmed}</li> - </ul> - </div> - ); - } -} - -interface ReserveListProps { - exchangeBaseUrl: string; -} - -interface ToggleProps { - expanded: StateHolder<boolean>; -} - -class Toggle extends ImplicitStateComponent<ToggleProps> { - renderButton() { - let show = () => { - this.props.expanded(true); - this.setState({}); - }; - let hide = () => { - this.props.expanded(false); - this.setState({}); - }; - if (this.props.expanded()) { - return <button onClick={hide}>hide</button>; - } - return <button onClick={show}>show</button>; - - } - render() { - return ( - <div style={{display: "inline"}}> - {this.renderButton()} - {this.props.expanded() ? this.props.children : []} - </div>); - } -} - - -interface CoinViewProps { - coin: Coin; -} - -interface RefreshDialogProps { - coin: Coin; -} - -class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> { - refreshRequested = this.makeState<boolean>(false); - render(): JSX.Element { - if (!this.refreshRequested()) { - return ( - <div style={{display: "inline"}}> - <button onClick={() => this.refreshRequested(true)}>refresh</button> - </div> - ); - } - return ( - <div> - Refresh amount: <input type="text" size={10} /> - <button onClick={() => refresh(this.props.coin.coinPub)}>ok</button> - <button onClick={() => this.refreshRequested(false)}>cancel</button> - </div> - ); - } -} - -class CoinView extends React.Component<CoinViewProps, void> { - render() { - let c = this.props.coin; - return ( - <div className="tree-item"> - <ul> - <li>Key: {c.coinPub}</li> - <li>Current amount: {prettyAmount(c.currentAmount)}</li> - <li>Denomination: {abbrev(c.denomPub, 20)}</li> - <li>Suspended: {(c.suspended || false).toString()}</li> - <li><RefreshDialog coin={c} /></li> - </ul> - </div> - ); - } -} - - - -interface PreCoinViewProps { - precoin: PreCoin; -} - -class PreCoinView extends React.Component<PreCoinViewProps, void> { - render() { - let c = this.props.precoin; - return ( - <div className="tree-item"> - <ul> - <li>Key: {c.coinPub}</li> - </ul> - </div> - ); - } -} - -interface CoinListProps { - exchangeBaseUrl: string; -} - -class CoinList extends ImplicitStateComponent<CoinListProps> { - coins = this.makeState<Coin[] | null>(null); - expanded = this.makeState<boolean>(false); - - constructor(props: CoinListProps) { - super(props); - this.update(props); - } - - async update(props: CoinListProps) { - let coins = await getCoins(props.exchangeBaseUrl); - this.coins(coins); - } - - componentWillReceiveProps(newProps: CoinListProps) { - this.update(newProps); - } - - render(): JSX.Element { - if (!this.coins()) { - return <div>...</div>; - } - return ( - <div className="tree-item"> - Coins ({this.coins() !.length.toString()}) - {" "} - <Toggle expanded={this.expanded}> - {this.coins() !.map((c) => <CoinView coin={c} />)} - </Toggle> - </div> - ); - } -} - - -interface PreCoinListProps { - exchangeBaseUrl: string; -} - -class PreCoinList extends ImplicitStateComponent<PreCoinListProps> { - precoins = this.makeState<PreCoin[] | null>(null); - expanded = this.makeState<boolean>(false); - - constructor(props: PreCoinListProps) { - super(props); - this.update(); - } - - async update() { - let precoins = await getPreCoins(this.props.exchangeBaseUrl); - this.precoins(precoins); - } - - render(): JSX.Element { - if (!this.precoins()) { - return <div>...</div>; - } - return ( - <div className="tree-item"> - Pre-Coins ({this.precoins() !.length.toString()}) - {" "} - <Toggle expanded={this.expanded}> - {this.precoins() !.map((c) => <PreCoinView precoin={c} />)} - </Toggle> - </div> - ); - } -} - -interface DenominationListProps { - exchange: IExchangeInfo; -} - -interface ExpanderTextProps { - text: string; -} - -class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> { - expanded = this.makeState<boolean>(false); - textArea: any = undefined; - - componentDidUpdate() { - if (this.expanded() && this.textArea) { - this.textArea.focus(); - this.textArea.scrollTop = 0; - } - } - - render(): JSX.Element { - if (!this.expanded()) { - return ( - <span onClick={() => { this.expanded(true); }}> - {(this.props.text.length <= 10) - ? this.props.text - : ( - <span> - {this.props.text.substring(0,10)} - <span style={{textDecoration: "underline"}}>...</span> - </span> - ) - } - </span> - ); - } - return ( - <textarea - readOnly - style={{display: "block"}} - onBlur={() => this.expanded(false)} - ref={(e) => this.textArea = e}> - {this.props.text} - </textarea> - ); - } -} - -class DenominationList extends ImplicitStateComponent<DenominationListProps> { - expanded = this.makeState<boolean>(false); - - renderDenom(d: Denomination) { - return ( - <div className="tree-item"> - <ul> - <li>Value: {prettyAmount(d.value)}</li> - <li>Withdraw fee: {prettyAmount(d.fee_withdraw)}</li> - <li>Refresh fee: {prettyAmount(d.fee_refresh)}</li> - <li>Deposit fee: {prettyAmount(d.fee_deposit)}</li> - <li>Refund fee: {prettyAmount(d.fee_refund)}</li> - <li>Start: {getTalerStampDate(d.stamp_start)!.toString()}</li> - <li>Withdraw expiration: {getTalerStampDate(d.stamp_expire_withdraw)!.toString()}</li> - <li>Legal expiration: {getTalerStampDate(d.stamp_expire_legal)!.toString()}</li> - <li>Deposit expiration: {getTalerStampDate(d.stamp_expire_deposit)!.toString()}</li> - <li>Denom pub: <ExpanderText text={d.denom_pub} /></li> - </ul> - </div> - ); - } - - render(): JSX.Element { - return ( - <div className="tree-item"> - Denominations ({this.props.exchange.active_denoms.length.toString()}) - {" "} - <Toggle expanded={this.expanded}> - {this.props.exchange.active_denoms.map((d) => this.renderDenom(d))} - </Toggle> - </div> - ); - } -} - -class ReserveList extends ImplicitStateComponent<ReserveListProps> { - reserves = this.makeState<ReserveRecord[] | null>(null); - expanded = this.makeState<boolean>(false); - - constructor(props: ReserveListProps) { - super(props); - this.update(); - } - - async update() { - let reserves = await getReserves(this.props.exchangeBaseUrl); - this.reserves(reserves); - } - - render(): JSX.Element { - if (!this.reserves()) { - return <div>...</div>; - } - return ( - <div className="tree-item"> - Reserves ({this.reserves() !.length.toString()}) - {" "} - <Toggle expanded={this.expanded}> - {this.reserves() !.map((r) => <ReserveView reserve={r} />)} - </Toggle> - </div> - ); - } -} - -interface ExchangeProps { - exchange: IExchangeInfo; -} - -class ExchangeView extends React.Component<ExchangeProps, void> { - render(): JSX.Element { - let e = this.props.exchange; - return ( - <div className="tree-item"> - <ul> - <li>Exchange Base Url: {this.props.exchange.baseUrl}</li> - <li>Master public key: <ExpanderText text={this.props.exchange.masterPublicKey} /></li> - </ul> - <DenominationList exchange={e} /> - <ReserveList exchangeBaseUrl={this.props.exchange.baseUrl} /> - <CoinList exchangeBaseUrl={this.props.exchange.baseUrl} /> - <PreCoinList exchangeBaseUrl={this.props.exchange.baseUrl} /> - </div> - ); - } -} - -interface ExchangesListState { - exchanges?: IExchangeInfo[]; -} - -class ExchangesList extends React.Component<any, ExchangesListState> { - constructor() { - super(); - let 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() { - let exchanges = await getExchanges(); - console.log("exchanges: ", exchanges); - this.setState({ exchanges }); - } - - render(): JSX.Element { - let exchanges = this.state.exchanges; - if (!exchanges) { - return <span>...</span>; - } - return ( - <div className="tree-item"> - Exchanges ({exchanges.length.toString()}): - {exchanges.map(e => <ExchangeView exchange={e} />)} - </div> - ); - } -} - -export function main() { - ReactDOM.render(<ExchangesList />, document.getElementById("container")!); -} |