diff options
Diffstat (limited to 'popup')
-rw-r--r-- | popup/popup.css | 84 | ||||
-rw-r--r-- | popup/popup.html | 26 | ||||
-rw-r--r-- | popup/popup.tsx | 508 |
3 files changed, 0 insertions, 618 deletions
diff --git a/popup/popup.css b/popup/popup.css deleted file mode 100644 index 675412c11..000000000 --- a/popup/popup.css +++ /dev/null @@ -1,84 +0,0 @@ - -/** - * @author Gabor X. Toth - * @author Marcello Stanisci - * @author Florian Dold - */ - -body { - min-height: 20em; - width: 30em; - margin: 0; - padding: 0; - max-height: 800px; - overflow: hidden; -} - -.nav { - background-color: #ddd; - padding: 0.5em 0; -} - -.nav a { - color: black; - padding: 0.5em; - text-decoration: none; -} - -.nav a.active { - background-color: white; - font-weight: bold; -} - - -.container { - overflow-y: scroll; - max-height: 400px; -} - -.abbrev { - text-decoration-style: dotted; -} - -#content { - padding: 1em; -} - - -#wallet-table .amount { - text-align: right; -} - -.hidden { - display: none; -} - -#transactions-table th, -#transactions-table td { - padding: 0.2em 0.5em; -} - -#reserve-create table { - width: 100%; -} - -#reserve-create table td.label { - width: 5em; -} - -#reserve-create table .input input[type="text"] { - width: 100%; -} - -.historyItem { - border: 1px solid black; - border-radius: 10px; - padding-left: 0.5em; - margin: 0.5em; -} - -.historyDate { - font-size: 90%; - margin: 0.3em; - color: slategray; -} diff --git a/popup/popup.html b/popup/popup.html deleted file mode 100644 index 9059c0da1..000000000 --- a/popup/popup.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <meta charset="utf-8"> - - <link rel="stylesheet" type="text/css" href="../style/lang.css"> - <link rel="stylesheet" type="text/css" href="popup.css"> - - <script src="../lib/vendor/react.js"></script> - <script src="../lib/vendor/react-dom.js"></script> - <script src="../lib/vendor/URI.js"></script> - - <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> -</head> - -<body> -<div id="content" style="margin:0;padding:0"></div> -</body> - -</html> diff --git a/popup/popup.tsx b/popup/popup.tsx deleted file mode 100644 index fa21d76e3..000000000 --- a/popup/popup.tsx +++ /dev/null @@ -1,508 +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 - */ - - -"use strict"; - -import {substituteFulfillmentUrl} from "../lib/wallet/helpers"; -import BrowserClickedEvent = chrome.browserAction.BrowserClickedEvent; -import {HistoryRecord, HistoryLevel} from "../lib/wallet/wallet"; -import { - AmountJson, WalletBalance, Amounts, - WalletBalanceEntry -} from "../lib/wallet/types"; -import {abbrev, prettyAmount} from "../lib/wallet/renderHtml"; - -declare var i18n: any; - -function onUpdateNotification(f: () => void): () => void { - let port = chrome.runtime.connect({name: "notifications"}); - let listener = (msg: any, port: any) => { - f(); - }; - port.onMessage.addListener(listener); - return () => { - port.onMessage.removeListener(listener); - } -} - - -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 () => { - let i = Router.routeHandlers.indexOf(f); - this.routeHandlers = this.routeHandlers.splice(i, 1); - } - } - - static routeHandlers: any[] = []; - - componentWillMount() { - console.log("router mounted"); - window.onhashchange = () => { - this.setState({}); - for (let f of Router.routeHandlers) { - f(); - } - } - } - - componentWillUnmount() { - console.log("router unmounted"); - } - - - render(): JSX.Element { - let 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) => { - let childProps: any = (child as any).props; - if (!childProps) { - return; - } - if (childProps["default"]) { - defaultChild = child; - } - if (childProps["route"] == route) { - foundChild = child; - } - }) - let child: React.ReactChild | null = foundChild || defaultChild; - if (!child) { - throw Error("unknown route"); - } - Router.setRoute((child as any).props["route"]); - return <div>{child}</div>; - } -} - -export function main() { - console.log("popup main"); - - let el = ( - <div> - <WalletNavBar /> - <div style={{margin: "1em"}}> - <Router> - <WalletBalanceView route="/balance" default/> - <WalletHistory route="/history"/> - <WalletDebug route="/debug"/> - </Router> - </div> - </div> - ); - - ReactDOM.render(el, document.getElementById("content")!); -} - -interface TabProps { - target: string; - children?: React.ReactNode; -} - -function Tab(props: TabProps) { - let cssClass = ""; - if (props.target == Router.getRoute()) { - cssClass = "active"; - } - let onClick = (e: React.MouseEvent) => { - 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> { - cancelSubscription: any; - - componentWillMount() { - this.cancelSubscription = Router.onRoute(() => { - this.setState({}); - }); - } - - componentWillUnmount() { - if (this.cancelSubscription) { - this.cancelSubscription(); - } - } - - render() { - console.log("rendering nav bar"); - return ( - <div className="nav" id="header"> - <Tab target="/balance"> - Balance - </Tab> - <Tab target="/history"> - History - </Tab> - <Tab target="/debug"> - Debug - </Tab> - </div>); - } -} - - -function ExtensionLink(props: any) { - let onClick = (e: React.MouseEvent) => { - chrome.tabs.create({ - "url": chrome.extension.getURL(props.target) - }); - e.preventDefault(); - }; - return ( - <a onClick={onClick} href={props.target}> - {props.children} - </a>) -} - -class WalletBalanceView extends React.Component<any, any> { - balance: WalletBalance; - gotError = false; - canceler: (() => void) | undefined = undefined; - unmount = false; - - componentWillMount() { - this.canceler = onUpdateNotification(() => this.updateBalance()); - this.updateBalance(); - } - - componentWillUnmount() { - console.log("component WalletBalanceView will unmount"); - if (this.canceler) { - this.canceler(); - } - this.unmount = true; - } - - updateBalance() { - chrome.runtime.sendMessage({type: "balances"}, (resp) => { - if (this.unmount) { - return; - } - if (resp.error) { - this.gotError = true; - console.error("could not retrieve balances", resp); - this.setState({}); - return; - } - this.gotError = false; - console.log("got wallet", resp); - this.balance = resp; - this.setState({}); - }); - } - - renderEmpty(): JSX.Element { - let helpLink = ( - <ExtensionLink target="pages/help/empty-wallet.html"> - help - </ExtensionLink> - ); - return <div>You have no balance to show. Need some - {" "}{helpLink}{" "} - getting started?</div>; - } - - formatPending(entry: WalletBalanceEntry): JSX.Element { - let incoming: JSX.Element | undefined; - let payment: JSX.Element | undefined; - - console.log("available: ", entry.pendingIncoming ? prettyAmount(entry.available) : null); - console.log("incoming: ", entry.pendingIncoming ? prettyAmount(entry.pendingIncoming) : null); - - if (Amounts.isNonZero(entry.pendingIncoming)) { - incoming = ( - <span> - <span style={{color: "darkgreen"}}> - {"+"} - {prettyAmount(entry.pendingIncoming)} - </span> - {" "} - incoming - </span>); - } - - if (Amounts.isNonZero(entry.pendingPayment)) { - payment = ( - <span> - <span style={{color: "darkblue"}}> - {prettyAmount(entry.pendingPayment)} - </span> - {" "} - being spent - </span>); - } - - let 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 { - let wallet = this.balance; - if (this.gotError) { - return i18n`Error: could not retrieve balance information.`; - } - if (!wallet) { - return <span></span>; - } - console.log(wallet); - let listing = Object.keys(wallet).map((key) => { - let entry: WalletBalanceEntry = wallet[key]; - return ( - <p> - {prettyAmount(entry.available)} - {" "} - {this.formatPending(entry)} - </p> - ); - }); - if (listing.length > 0) { - return <div>{listing}</div>; - } - - return this.renderEmpty(); - } -} - - -function formatHistoryItem(historyItem: HistoryRecord) { - const d = historyItem.detail; - const t = historyItem.timestamp; - console.log("hist item", historyItem); - switch (historyItem.type) { - case "create-reserve": - return ( - <p> - {i18n.parts`Bank requested reserve (${abbrev(d.reservePub)}) for ${prettyAmount( - d.requestedAmount)}.`} - </p> - ); - case "confirm-reserve": { - // FIXME: eventually remove compat fix - let exchange = d.exchangeBaseUrl ? URI(d.exchangeBaseUrl).host() : "??"; - let amount = prettyAmount(d.requestedAmount); - let pub = abbrev(d.reservePub); - return ( - <p> - {i18n.parts`Started to withdraw ${amount} from ${exchange} (${pub}).`} - </p> - ); - } - case "offer-contract": { - let link = chrome.extension.getURL("view-contract.html"); - let linkElem = <a href={link}>{abbrev(d.contractHash)}</a>; - let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>; - return ( - <p> - {i18n.parts`Merchant ${merchantElem} offered contract ${linkElem}.`} - </p> - ); - } - case "depleted-reserve": { - let exchange = d.exchangeBaseUrl ? URI(d.exchangeBaseUrl).host() : "??"; - let amount = prettyAmount(d.requestedAmount); - let pub = abbrev(d.reservePub); - return (<p> - {i18n.parts`Withdrew ${amount} from ${exchange} (${pub}).`} - </p>); - } - case "pay": { - let url = substituteFulfillmentUrl(d.fulfillmentUrl, - {H_contract: d.contractHash}); - let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>; - let fulfillmentLinkElem = <a href={url} onClick={openTab(url)}>view product</a>; - return ( - <p> - {i18n.parts`Paid ${prettyAmount(d.amount)} to merchant ${merchantElem}. (${fulfillmentLinkElem})`} - </p>); - } - default: - return (<p>i18n`Unknown event (${historyItem.type})`</p>); - } -} - - -class WalletHistory extends React.Component<any, any> { - myHistory: any[]; - gotError = false; - unmounted = false; - - componentWillMount() { - this.update(); - onUpdateNotification(() => this.update()); - } - - componentWillUnmount() { - console.log("history component unmounted"); - this.unmounted = true; - } - - update() { - chrome.runtime.sendMessage({type: "get-history"}, (resp) => { - if (this.unmounted) { - return; - } - console.log("got history response"); - if (resp.error) { - this.gotError = true; - console.error("could not retrieve history", resp); - this.setState({}); - return; - } - this.gotError = false; - console.log("got history", resp.history); - this.myHistory = resp.history; - this.setState({}); - }); - } - - render(): JSX.Element { - console.log("rendering history"); - let history: HistoryRecord[] = this.myHistory; - if (this.gotError) { - return i18n`Error: could not retrieve event history`; - } - - if (!history) { - // We're not ready yet - return <span />; - } - - let subjectMemo: {[s: string]: boolean} = {}; - let listing: any[] = []; - for (let record of history.reverse()) { - if (record.subjectId && subjectMemo[record.subjectId]) { - continue; - } - if (record.level != undefined && record.level < HistoryLevel.User) { - continue; - } - subjectMemo[record.subjectId as string] = true; - - let item = ( - <div className="historyItem"> - <div className="historyDate"> - {(new Date(record.timestamp)).toString()} - </div> - {formatHistoryItem(record)} - </div> - ); - - listing.push(item); - } - - if (listing.length > 0) { - return <div className="container">{listing}</div>; - } - return <p>{i18n`Your wallet has no events recorded.`}</p> - } - -} - - -function reload() { - try { - chrome.runtime.reload(); - window.close(); - } catch (e) { - // Functionality missing in firefox, ignore! - } -} - -function confirmReset() { - if (confirm("Do you want to IRREVOCABLY DESTROY everything inside your" + - " wallet and LOSE ALL YOUR COINS?")) { - chrome.runtime.sendMessage({type: "reset"}); - window.close(); - } -} - - -function WalletDebug(props: any) { - return (<div> - <p>Debug tools:</p> - <button onClick={openExtensionPage("popup/popup.html")}> - wallet tab - </button> - <button onClick={openExtensionPage("pages/show-db.html")}> - show db - </button> - <button onClick={openExtensionPage("pages/tree.html")}> - show tree - </button> - <br /> - <button onClick={confirmReset}> - reset - </button> - <button onClick={reload}> - reload chrome extension - </button> - </div>); -} - - -function openExtensionPage(page: string) { - return function() { - chrome.tabs.create({ - "url": chrome.extension.getURL(page) - }); - } -} - - -function openTab(page: string) { - return function() { - chrome.tabs.create({ - "url": page - }); - } -} |