From db0fc776982a61181baf286c8344a3b11d3531ec Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 10 Oct 2016 00:37:08 +0200 Subject: refactor / remove lodash dependency --- popup/popup.tsx | 406 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 246 insertions(+), 160 deletions(-) (limited to 'popup/popup.tsx') diff --git a/popup/popup.tsx b/popup/popup.tsx index 5b2b1e9f9..5fcc871e2 100644 --- a/popup/popup.tsx +++ b/popup/popup.tsx @@ -23,9 +23,6 @@ */ -/// -/// - "use strict"; import {substituteFulfillmentUrl} from "../lib/wallet/helpers"; @@ -33,10 +30,8 @@ import BrowserClickedEvent = chrome.browserAction.BrowserClickedEvent; import {HistoryRecord, HistoryLevel} from "../lib/wallet/wallet"; import {AmountJson} from "../lib/wallet/types"; -declare var m: any; declare var i18n: any; - function onUpdateNotification(f: () => void) { let port = chrome.runtime.connect({name: "notifications"}); port.onMessage.addListener((msg, port) => { @@ -45,116 +40,198 @@ function onUpdateNotification(f: () => void) { } +class Router extends preact.Component { + static setRoute(s: string): void { + window.location.hash = s; + preact.rerender(); + } + + static getRoute(): string { + // Omit the '#' at the beginning + return window.location.hash.substring(1); + } + + static onRoute(f: any): () => void { + this.routeHandlers.push(f); + return () => { + let i = this.routeHandlers.indexOf(f); + this.routeHandlers = this.routeHandlers.splice(i, 1); + } + } + + static routeHandlers: any[] = []; + + componentWillMount() { + console.log("router mounted"); + window.onhashchange = () => { + this.forceUpdate(); + for (let f of Router.routeHandlers) { + f(); + } + } + } + + componentWillUnmount() { + console.log("router unmounted"); + } + + + render(props: any, state: any): JSX.Element { + let route = window.location.hash.substring(1); + console.log("rendering route", route); + let defaultChild: JSX.Element|null = null; + for (let child of props.children) { + if (child.attributes["default"]) { + defaultChild = child; + } + if (child.attributes["route"] == route) { + return
{child}
; + } + } + if (defaultChild == null) { + throw Error("unknown route"); + } + console.log("rendering default route"); + Router.setRoute(defaultChild.attributes["route"]); + return
{defaultChild}
; + } +} + export function main() { console.log("popup main"); - m.route.mode = "hash"; - m.route(document.getElementById("content"), "/balance", { - "/balance": WalletBalance, - "/history": WalletHistory, - "/debug": WalletDebug, - }); - m.mount(document.getElementById("nav"), WalletNavBar); -} -console.log("this is popup"); + let el = ( +
+ + + + + + +
+ ); + + preact.render(el, document.getElementById("content")!); +} +interface TabProps extends preact.ComponentProps { + target: string; +} -function makeTab(target: string, name: string) { +function Tab(props: TabProps) { let cssClass = ""; - if (target == m.route()) { + if (props.target == Router.getRoute()) { cssClass = "active"; } - return m("a", {config: m.route, href: target, "class": cssClass}, name); + let onClick = (e: Event) => { + Router.setRoute(props.target); + e.preventDefault(); + }; + return ( + + {props.children} + + ); } -namespace WalletNavBar { - export function view() { - return m("div#header.nav", [ - makeTab("/balance", i18n`Balance`), - makeTab("/history", i18n`History`), - makeTab("/debug", i18n`Debug`), - ]); + +class WalletNavBar extends preact.Component { + cancelSubscription: any; + + componentWillMount() { + this.cancelSubscription = Router.onRoute(() => { + this.setState({}); + }); } - export function controller() { - // empty + componentWillUnmount() { + if (this.cancelSubscription) { + this.cancelSubscription(); + } + } + + render() { + console.log("rendering nav bar"); + return ( + ); } } -function openInExtension(element: HTMLAnchorElement, isInitialized: boolean) { - element.addEventListener("click", (e: Event) => { +function ExtensionLink(props: any) { + let onClick = (e: Event) => { chrome.tabs.create({ - "url": element.href + "url": chrome.extension.getURL(props.target) }); e.preventDefault(); - }); + }; + return ( + + {props.children} + ) } +class WalletBalance extends preact.Component { + myWallet: any; + gotError = false; -namespace WalletBalance { - export function controller() { - return new Controller(); - } - - class Controller { - myWallet: any; - gotError = false; + componentWillMount() { + this.updateBalance(); - constructor() { - this.updateBalance(); - - onUpdateNotification(() => this.updateBalance()); - } + onUpdateNotification(() => this.updateBalance()); + } - updateBalance() { - m.startComputation(); - chrome.runtime.sendMessage({type: "balances"}, (resp) => { - if (resp.error) { - this.gotError = true; - console.error("could not retrieve balances", resp); - m.endComputation(); - return; - } - this.gotError = false; - console.log("got wallet", resp); - this.myWallet = resp.balances; - m.endComputation(); - }); - } + updateBalance() { + chrome.runtime.sendMessage({type: "balances"}, (resp) => { + if (resp.error) { + this.gotError = true; + console.error("could not retrieve balances", resp); + this.forceUpdate(); + return; + } + this.gotError = false; + console.log("got wallet", resp); + this.myWallet = resp.balances; + this.forceUpdate(); + }); } - export function view(ctrl: Controller) { - let wallet = ctrl.myWallet; - if (ctrl.gotError) { + render(): JSX.Element { + let wallet = this.myWallet; + if (this.gotError) { return i18n`Error: could not retrieve balance information.`; } if (!wallet) { - throw Error("Could not retrieve wallet"); + return
} - let listing = _.map(wallet, (x: any) => m("p", formatAmount(x))); + console.log(wallet); + let listing = Object.keys(wallet).map((key) => { + return

{formatAmount(wallet[key])}

+ }); if (listing.length > 0) { - return listing; + return
{listing}
; } - let helpLink = m("a", - { - config: openInExtension, - href: chrome.extension.getURL( - "pages/help/empty-wallet.html") - }, - i18n`help`); + let helpLink = ( + + help + + ); return i18n.parts`You have no balance to show. Need some ${helpLink} getting started?`; } } -function formatTimestamp(t: number) { - let x = new Date(t); - return x.toLocaleString(); -} - - function formatAmount(amount: AmountJson) { let v = amount.value + amount.fraction / 1e6; return `${v.toFixed(2)} ${amount.currency}`; @@ -166,17 +243,11 @@ function abbrev(s: string, n: number = 5) { if (s.length > n) { sAbbrev = s.slice(0, n) + ".."; } - return m("span.abbrev", {title: s}, sAbbrev); -} - - -function retryPayment(url: string, contractHash: string) { - return function() { - chrome.tabs.create({ - "url": substituteFulfillmentUrl(url, - {H_contract: contractHash}) - }); - } + return ( + + {sAbbrev} + + ); } @@ -186,78 +257,85 @@ function formatHistoryItem(historyItem: HistoryRecord) { console.log("hist item", historyItem); switch (historyItem.type) { case "create-reserve": - return m("p", - i18n.parts`Bank requested reserve (${abbrev(d.reservePub)}) for ${formatAmount( - d.requestedAmount)}.`); + return ( +

+ {i18n.parts`Bank requested reserve (${abbrev(d.reservePub)}) for ${formatAmount( + d.requestedAmount)}.`} +

+ ); case "confirm-reserve": - return m("p", - i18n.parts`Started to withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( - d.requestedAmount)}.`); + return ( +

+ {i18n.parts`Started to withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( + d.requestedAmount)}.`} +

+ ); case "offer-contract": { let link = chrome.extension.getURL("view-contract.html"); - let linkElem = m("a", {href: link}, abbrev(d.contractHash)); - let merchantElem = m("em", abbrev(d.merchantName, 15)); - return m("p", - i18n.parts`Merchant ${merchantElem} offered contract ${linkElem}.`); + let linkElem = {abbrev(d.contractHash)}; + let merchantElem = {abbrev(d.merchantName, 15)}; + return ( +

+ {i18n.parts`Merchant ${merchantElem} offered contract ${linkElem}.`} +

+ ); } case "depleted-reserve": - return m("p", - i18n.parts`Withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( - d.requestedAmount)} completed.`); + return (

+ {i18n.parts`Withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( + d.requestedAmount)} completed.`} +

); case "pay": { let url = substituteFulfillmentUrl(d.fulfillmentUrl, {H_contract: d.contractHash}); - let merchantElem = m("em", abbrev(d.merchantName, 15)); - let fulfillmentLinkElem = m(`a`, - {href: url, onclick: openTab(url)}, - "view product"); - return m("p", - i18n.parts`Confirmed payment of ${formatAmount(d.amount)} to merchant ${merchantElem}. (${fulfillmentLinkElem})`); + let merchantElem = {abbrev(d.merchantName, 15)}; + let fulfillmentLinkElem = view product; + return ( +

+ {i18n.parts`Confirmed payment of ${formatAmount(d.amount)} to merchant ${merchantElem}. (${fulfillmentLinkElem})`} +

); } default: - return m("p", i18n`Unknown event (${historyItem.type})`); + return (

i18n`Unknown event (${historyItem.type})`

); } } -namespace WalletHistory { - export function controller() { - return new Controller(); - } - - class Controller { - myHistory: any[]; - gotError = false; +class WalletHistory extends preact.Component { + myHistory: any[]; + gotError = false; - constructor() { - this.update(); - onUpdateNotification(() => this.update()); - } + componentWillMount() { + this.update(); + onUpdateNotification(() => this.update()); + } - update() { - m.startComputation(); - chrome.runtime.sendMessage({type: "get-history"}, (resp) => { - if (resp.error) { - this.gotError = true; - console.error("could not retrieve history", resp); - m.endComputation(); - return; - } - this.gotError = false; - console.log("got history", resp.history); - this.myHistory = resp.history; - m.endComputation(); - }); - } + update() { + chrome.runtime.sendMessage({type: "get-history"}, (resp) => { + console.log("got history response"); + if (resp.error) { + this.gotError = true; + console.error("could not retrieve history", resp); + this.forceUpdate(); + return; + } + this.gotError = false; + console.log("got history", resp.history); + this.myHistory = resp.history; + this.forceUpdate(); + }); } - export function view(ctrl: Controller) { - let history: HistoryRecord[] = ctrl.myHistory; - if (ctrl.gotError) { + 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) { - throw Error("Could not retrieve history"); + // We're not ready yet + return ; } let subjectMemo: {[s: string]: boolean} = {}; @@ -271,19 +349,24 @@ namespace WalletHistory { } subjectMemo[record.subjectId as string] = true; - let item = m("div.historyItem", {}, [ - m("div.historyDate", {}, (new Date(record.timestamp)).toString()), - formatHistoryItem(record) - ]); + let item = ( +
+
+ {(new Date(record.timestamp)).toString()} +
+ {formatHistoryItem(record)} +
+ ); listing.push(item); } if (listing.length > 0) { - return m("div.container", listing); + return
{listing}
; } - return i18n`Your wallet has no events recorded.`; + return

{i18n`Your wallet has no events recorded.`}

} + } @@ -305,21 +388,24 @@ function confirmReset() { } -var WalletDebug = { - view() { - return [ - m("button", - {onclick: openExtensionPage("popup/popup.html")}, - "wallet tab"), - m("button", - {onclick: openExtensionPage("pages/show-db.html")}, - "show db"), - m("br"), - m("button", {onclick: confirmReset}, "reset"), - m("button", {onclick: reload}, "reload chrome extension"), - ] - } -}; +function WalletDebug(props: any) { + return (
+

Debug tools:

+ + +
+ + +
); +} function openExtensionPage(page: string) { -- cgit v1.2.3