diff options
Diffstat (limited to 'src/webex')
-rw-r--r-- | src/webex/chromeBadge.ts | 22 | ||||
-rw-r--r-- | src/webex/notify.ts | 16 | ||||
-rw-r--r-- | src/webex/pages/add-auditor.tsx | 4 | ||||
-rw-r--r-- | src/webex/pages/benchmark.tsx | 10 | ||||
-rw-r--r-- | src/webex/pages/pay.tsx | 10 | ||||
-rw-r--r-- | src/webex/pages/popup.tsx | 68 | ||||
-rw-r--r-- | src/webex/pages/refund.tsx | 13 | ||||
-rw-r--r-- | src/webex/pages/return-coins.tsx | 17 | ||||
-rw-r--r-- | src/webex/pages/tip.tsx | 19 | ||||
-rw-r--r-- | src/webex/pages/welcome.tsx | 8 | ||||
-rw-r--r-- | src/webex/pages/withdraw.tsx | 29 | ||||
-rw-r--r-- | src/webex/renderHtml.tsx | 28 | ||||
-rw-r--r-- | src/webex/wxApi.ts | 11 | ||||
-rw-r--r-- | src/webex/wxBackend.ts | 89 |
14 files changed, 158 insertions, 186 deletions
diff --git a/src/webex/chromeBadge.ts b/src/webex/chromeBadge.ts index 330388ca0..7bc5d368d 100644 --- a/src/webex/chromeBadge.ts +++ b/src/webex/chromeBadge.ts @@ -20,7 +20,7 @@ import { isFirefox } from "./compat"; * Polyfill for requestAnimationFrame, which * doesn't work from a background page. */ -function rAF(cb: (ts: number) => void) { +function rAF(cb: (ts: number) => void): void { window.setTimeout(() => { cb(performance.now()); }, 100 /* 100 ms delay between frames */); @@ -99,14 +99,18 @@ export class ChromeBadge { // size in draw() as well! this.canvas.width = 32; this.canvas.height = 32; - this.ctx = this.canvas.getContext("2d")!; + const ctx = this.canvas.getContext("2d"); + if (!ctx) { + throw Error("unable to get canvas context"); + } + this.ctx = ctx; this.draw(); } /** * Draw the badge based on the current state. */ - private draw() { + private draw(): void { this.ctx.setTransform(1, 0, 0, 1, 0, 0); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); @@ -202,7 +206,7 @@ export class ChromeBadge { } } - private animate() { + private animate(): void { if (this.animationRunning) { return; } @@ -212,7 +216,7 @@ export class ChromeBadge { } this.animationRunning = true; let start: number | undefined; - const step = (timestamp: number) => { + const step = (timestamp: number): void => { if (!this.animationRunning) { return; } @@ -257,7 +261,7 @@ export class ChromeBadge { * Draw the badge such that it shows the * user that something happened (balance changed). */ - showNotification() { + showNotification(): void { this.hasNotification = true; this.draw(); } @@ -265,12 +269,12 @@ export class ChromeBadge { /** * Draw the badge without the notification mark. */ - clearNotification() { + clearNotification(): void { this.hasNotification = false; this.draw(); } - startBusy() { + startBusy(): void { if (this.isBusy) { return; } @@ -278,7 +282,7 @@ export class ChromeBadge { this.animate(); } - stopBusy() { + stopBusy(): void { this.isBusy = false; } } diff --git a/src/webex/notify.ts b/src/webex/notify.ts index 887658ef9..9fb0529db 100644 --- a/src/webex/notify.ts +++ b/src/webex/notify.ts @@ -47,7 +47,7 @@ const handlers: Handler[] = []; let sheet: CSSStyleSheet | null; -function initStyle() { +function initStyle(): void { logVerbose && console.log("taking over styles"); const name = "taler-presence-stylesheet"; const content = "/* Taler stylesheet controlled by JS */"; @@ -78,7 +78,7 @@ function initStyle() { } } -function setStyles(installed: boolean) { +function setStyles(installed: boolean): void { if (!sheet || !sheet.cssRules) { return; } @@ -93,7 +93,7 @@ function setStyles(installed: boolean) { } } -function onceOnComplete(cb: () => void) { +function onceOnComplete(cb: () => void): void { if (document.readyState === "complete") { cb(); } else { @@ -105,7 +105,7 @@ function onceOnComplete(cb: () => void) { } } -function init() { +function init(): void { onceOnComplete(() => { if (document.documentElement.getAttribute("data-taler-nojs")) { initStyle(); @@ -129,13 +129,13 @@ function init() { type HandlerFn = (detail: any, sendResponse: (msg: any) => void) => void; -function registerHandlers() { +function registerHandlers(): void { /** * Add a handler for a DOM event, which automatically * handles adding sequence numbers to responses. */ - function addHandler(type: string, handler: HandlerFn) { - const handlerWrap = (e: Event) => { + function addHandler(type: string, handler: HandlerFn): void { + const handlerWrap = (e: Event): void => { if (!(e instanceof Event)) { console.log("unexpected event", e); throw Error(`invariant violated`); @@ -154,7 +154,7 @@ function registerHandlers() { callId = e.detail.callId; detail = e.detail; } - const responder = (msg?: any) => { + const responder = (msg?: any): void => { const fullMsg = Object.assign({}, msg, { callId }); let opts = { detail: fullMsg }; if ("function" === typeof cloneInto) { diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx index dbe84cde4..4e3f8615c 100644 --- a/src/webex/pages/add-auditor.tsx +++ b/src/webex/pages/add-auditor.tsx @@ -31,10 +31,10 @@ interface ConfirmAuditorProps { expirationStamp: number; } -function ConfirmAuditor(props: ConfirmAuditorProps) { +function ConfirmAuditor(props: ConfirmAuditorProps): JSX.Element { const [addDone, setAddDone] = useState(false); - const add = async () => { + const add = async (): Promise<void> => { const currencies = await getCurrencies(); let currency: CurrencyRecord | undefined; diff --git a/src/webex/pages/benchmark.tsx b/src/webex/pages/benchmark.tsx index bf4c4b04d..eb7193e0c 100644 --- a/src/webex/pages/benchmark.tsx +++ b/src/webex/pages/benchmark.tsx @@ -34,7 +34,7 @@ interface BenchmarkRunnerState { running: boolean; } -function BenchmarkDisplay(props: BenchmarkRunnerState) { +function BenchmarkDisplay(props: BenchmarkRunnerState): JSX.Element { const result = props.result; if (!result) { if (props.running) { @@ -55,7 +55,7 @@ function BenchmarkDisplay(props: BenchmarkRunnerState) { {Object.keys(result.time) .sort() .map((k) => ( - <tr> + <tr key={k}> <td>{k}</td> <td>{result.time[k] / result.repetitions}</td> </tr> @@ -75,13 +75,13 @@ class BenchmarkRunner extends React.Component<any, BenchmarkRunnerState> { }; } - async run() { + async run(): Promise<void> { this.setState({ result: undefined, running: true }); const result = await wxApi.benchmarkCrypto(this.state.repetitions); this.setState({ result, running: false }); } - render() { + render(): JSX.Element { return ( <div> <label>Repetitions:</label> @@ -99,6 +99,6 @@ class BenchmarkRunner extends React.Component<any, BenchmarkRunnerState> { } } -export function makeBenchmarkPage() { +export function makeBenchmarkPage(): JSX.Element { return <BenchmarkRunner />; } diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx index 09aa595c3..e3dd630b6 100644 --- a/src/webex/pages/pay.tsx +++ b/src/webex/pages/pay.tsx @@ -34,7 +34,7 @@ import React, { useState, useEffect } from "react"; import * as Amounts from "../../util/amounts"; import { codecForContractTerms, ContractTerms } from "../../types/talerTypes"; -function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) { +function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element { const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(); const [payErrMsg, setPayErrMsg] = useState<string | undefined>(""); const [numTries, setNumTries] = useState(0); @@ -42,7 +42,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) { let totalFees: Amounts.AmountJson | undefined = undefined; useEffect(() => { - const doFetch = async () => { + const doFetch = async (): Promise<void> => { const p = await wxApi.preparePay(talerPayUri); setPayStatus(p); }; @@ -108,7 +108,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) { <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong> ); - const doPayment = async () => { + const doPayment = async (): Promise<void> => { if (payStatus.status !== "payment-possible") { throw Error(`invalid state: ${payStatus.status}`); } @@ -178,11 +178,11 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) { ); } -export function makePayPage() { +export function makePayPage(): 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} />; -}
\ No newline at end of file +} diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx index 6cd7242ff..c2f050e2a 100644 --- a/src/webex/pages/popup.tsx +++ b/src/webex/pages/popup.tsx @@ -46,7 +46,7 @@ import { Timestamp } from "../../util/time"; function onUpdateNotification(f: () => void): () => void { const port = chrome.runtime.connect({ name: "notifications" }); - const listener = () => { + const listener = (): void => { f(); }; port.onMessage.addListener(listener); @@ -75,7 +75,7 @@ class Router extends React.Component<any, any> { private static routeHandlers: any[] = []; - componentWillMount() { + componentWillMount(): void { console.log("router mounted"); window.onhashchange = () => { this.setState({}); @@ -85,10 +85,6 @@ class Router extends React.Component<any, any> { }; } - componentWillUnmount() { - console.log("router unmounted"); - } - render(): JSX.Element { const route = window.location.hash.substring(1); console.log("rendering route", route); @@ -120,12 +116,12 @@ interface TabProps { children?: React.ReactNode; } -function Tab(props: TabProps) { +function Tab(props: TabProps): JSX.Element { let cssClass = ""; if (props.target === Router.getRoute()) { cssClass = "active"; } - const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => { + const onClick = (e: React.MouseEvent<HTMLAnchorElement>): void => { Router.setRoute(props.target); e.preventDefault(); }; @@ -139,19 +135,19 @@ function Tab(props: TabProps) { class WalletNavBar extends React.Component<any, any> { private cancelSubscription: any; - componentWillMount() { + componentWillMount(): void { this.cancelSubscription = Router.onRoute(() => { this.setState({}); }); } - componentWillUnmount() { + componentWillUnmount(): void { if (this.cancelSubscription) { this.cancelSubscription(); } } - render() { + render(): JSX.Element { console.log("rendering nav bar"); return ( <div className="nav" id="header"> @@ -163,20 +159,6 @@ class WalletNavBar extends React.Component<any, any> { } } -function ExtensionLink(props: any) { - const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => { - chrome.tabs.create({ - url: chrome.extension.getURL(props.target), - }); - e.preventDefault(); - }; - return ( - <a onClick={onClick} href={props.target}> - {props.children} - </a> - ); -} - /** * Render an amount as a large number with a small currency symbol. */ @@ -190,7 +172,7 @@ function bigAmount(amount: AmountJson): JSX.Element { ); } -function EmptyBalanceView() { +function EmptyBalanceView(): JSX.Element { return ( <i18n.Translate wrap="p"> You have no balance to show. Need some{" "} @@ -205,12 +187,12 @@ class WalletBalanceView extends React.Component<any, any> { private canceler: (() => void) | undefined = undefined; private unmount = false; - componentWillMount() { + componentWillMount(): void { this.canceler = onUpdateNotification(() => this.updateBalance()); this.updateBalance(); } - componentWillUnmount() { + componentWillUnmount(): void { console.log("component WalletBalanceView will unmount"); if (this.canceler) { this.canceler(); @@ -218,7 +200,7 @@ class WalletBalanceView extends React.Component<any, any> { this.unmount = true; } - async updateBalance() { + async updateBalance(): Promise<void> { let balance: WalletBalance; try { balance = await wxApi.getBalance(); @@ -325,11 +307,11 @@ class WalletBalanceView extends React.Component<any, any> { } } -function Icon({ l }: { l: string }) { +function Icon({ l }: { l: string }): JSX.Element { return <div className={"icon"}>{l}</div>; } -function formatAndCapitalize(text: string) { +function formatAndCapitalize(text: string): string { text = text.replace("-", " "); text = text.replace(/^./, text[0].toUpperCase()); return text; @@ -357,8 +339,8 @@ function HistoryItem({ timestamp, icon, negative = false, -}: HistoryItemProps) { - function formatDate(timestamp: number | "never") { +}: HistoryItemProps): JSX.Element { + function formatDate(timestamp: number | "never"): string | null { if (timestamp !== "never") { const itemDate = moment(timestamp); if (itemDate.isBetween(moment().subtract(2, "days"), moment())) { @@ -444,7 +426,7 @@ function parseSummary(summary: string) { }; } -function formatHistoryItem(historyItem: HistoryEvent) { +function formatHistoryItem(historyItem: HistoryEvent): JSX.Element { switch (historyItem.type) { case "refreshed": { return ( @@ -637,7 +619,7 @@ function formatHistoryItem(historyItem: HistoryEvent) { } } -const HistoryComponent = (props: any) => { +const HistoryComponent = (props: any): JSX.Element => { const record = props.record; return formatHistoryItem(record); }; @@ -655,18 +637,18 @@ class WalletHistory extends React.Component<any, any> { "exchange-added", ]; - componentWillMount() { + componentWillMount(): void { this.update(); this.setState({ filter: true }); onUpdateNotification(() => this.update()); } - componentWillUnmount() { + componentWillUnmount(): void { console.log("history component unmounted"); this.unmounted = true; } - update() { + update(): void { chrome.runtime.sendMessage({ type: "get-history" }, (resp) => { if (this.unmounted) { return; @@ -727,7 +709,7 @@ class WalletHistory extends React.Component<any, any> { } } -function reload() { +function reload(): void { try { chrome.runtime.reload(); window.close(); @@ -736,7 +718,7 @@ function reload() { } } -function confirmReset() { +function confirmReset(): void { if ( confirm( "Do you want to IRREVOCABLY DESTROY everything inside your" + @@ -748,7 +730,7 @@ function confirmReset() { } } -function WalletDebug(props: any) { +function WalletDebug(props: any): JSX.Element { return ( <div> <p>Debug tools:</p> @@ -791,7 +773,7 @@ function openTab(page: string) { }; } -function WalletPopup() { +function WalletPopup(): JSX.Element { return ( <div> <WalletNavBar /> @@ -806,7 +788,7 @@ function WalletPopup() { ); } -export function createPopup() { +export function createPopup(): JSX.Element { chrome.runtime.connect({ name: "popup" }); return <WalletPopup />; }
\ No newline at end of file diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx index 8263ceace..4a13317cd 100644 --- a/src/webex/pages/refund.tsx +++ b/src/webex/pages/refund.tsx @@ -21,13 +21,12 @@ */ import React, { useEffect, useState } from "react"; -import ReactDOM from "react-dom"; import * as wxApi from "../wxApi"; import { PurchaseDetails } from "../../types/walletTypes"; import { AmountView } from "../renderHtml"; -function RefundStatusView(props: { talerRefundUri: string }) { +function RefundStatusView(props: { talerRefundUri: string }): JSX.Element { const [applied, setApplied] = useState(false); const [purchaseDetails, setPurchaseDetails] = useState< PurchaseDetails | undefined @@ -35,7 +34,7 @@ function RefundStatusView(props: { talerRefundUri: string }) { const [errMsg, setErrMsg] = useState<string | undefined>(undefined); useEffect(() => { - const doFetch = async () => { + const doFetch = async (): Promise<void> => { try { const hc = await wxApi.applyRefund(props.talerRefundUri); setApplied(true); @@ -73,19 +72,17 @@ function RefundStatusView(props: { talerRefundUri: string }) { ); } -export function createRefundPage() { +export function createRefundPage(): JSX.Element { const url = new URL(document.location.href); const container = document.getElementById("container"); if (!container) { - console.error("fatal: can't mount component, container missing"); - return; + throw Error("fatal: can't mount component, container missing") } const talerRefundUri = url.searchParams.get("talerRefundUri"); if (!talerRefundUri) { - console.error("taler refund URI requred"); - return; + throw Error("taler refund URI requred"); } return <RefundStatusView talerRefundUri={talerRefundUri} />; diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx index 06a3ba169..7d759705f 100644 --- a/src/webex/pages/return-coins.tsx +++ b/src/webex/pages/return-coins.tsx @@ -38,7 +38,6 @@ import { getBalance, getSenderWireInfos, returnCoins } from "../wxApi"; import { renderAmount } from "../renderHtml"; import * as React from "react"; -import * as ReactDOM from "react-dom"; interface ReturnSelectionItemProps extends ReturnSelectionListProps { exchangeUrl: string; @@ -129,7 +128,7 @@ class ReturnSelectionItem extends React.Component< ); } - select() { + select(): void { let val: number; let selectedWire: number; try { @@ -188,7 +187,7 @@ interface ReturnConfirmationProps { } class ReturnConfirmation extends React.Component<ReturnConfirmationProps, {}> { - render() { + render(): JSX.Element { return ( <div> <p> @@ -238,7 +237,7 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> { this.state = {} as any; } - async update() { + async update(): Promise<void> { const balance = await getBalance(); const senderWireInfos = await getSenderWireInfos(); console.log("got swi", senderWireInfos); @@ -246,11 +245,11 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> { this.setState({ balance, senderWireInfos }); } - selectDetail(d: SelectedDetail) { + selectDetail(d: SelectedDetail): void { this.setState({ selectedReturn: d }); } - async confirm() { + async confirm(): Promise<void> { const selectedReturn = this.state.selectedReturn; if (!selectedReturn) { return; @@ -263,14 +262,14 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> { }); } - async cancel() { + async cancel(): Promise<void> { this.setState({ selectedReturn: undefined, lastConfirmedDetail: undefined, }); } - render() { + render(): JSX.Element { const balance = this.state.balance; const senderWireInfos = this.state.senderWireInfos; if (!balance || !senderWireInfos) { @@ -310,6 +309,6 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> { } } -export function createReturnCoinsPage() { +export function createReturnCoinsPage(): JSX.Element { return <ReturnCoins />; } diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx index 10e12d590..9c797f50d 100644 --- a/src/webex/pages/tip.tsx +++ b/src/webex/pages/tip.tsx @@ -22,30 +22,25 @@ */ import * as React from "react"; -import * as ReactDOM from "react-dom"; -import * as i18n from "../i18n"; - -import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi"; +import { acceptTip, getTipStatus } from "../wxApi"; import { - WithdrawDetailView, renderAmount, ProgressButton, } from "../renderHtml"; -import * as Amounts from "../../util/amounts"; import { useState, useEffect } from "react"; import { TipStatus } from "../../types/walletTypes"; -function TipDisplay(props: { talerTipUri: string }) { +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 () => { + const doFetch = async (): Promise<void> => { const ts = await getTipStatus(props.talerTipUri); setTipStatus(ts); }; @@ -53,7 +48,7 @@ function TipDisplay(props: { talerTipUri: string }) { }, []); if (discarded) { - return <span>You've discarded the tip.</span>; + return <span>You've discarded the tip.</span>; } if (finished) { @@ -64,11 +59,11 @@ function TipDisplay(props: { talerTipUri: string }) { return <span>Loading ...</span>; } - const discard = () => { + const discard = (): void => { setDiscarded(true); }; - const accept = async () => { + const accept = async (): Promise<void> => { setLoading(true); await acceptTip(tipStatus.tipId); setFinished(true); @@ -100,7 +95,7 @@ function TipDisplay(props: { talerTipUri: string }) { ); } -export function createTipPage() { +export function createTipPage(): JSX.Element { const url = new URL(document.location.href); const talerTipUri = url.searchParams.get("talerTipUri"); if (typeof talerTipUri !== "string") { diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx index 8510ad383..a99cadb05 100644 --- a/src/webex/pages/welcome.tsx +++ b/src/webex/pages/welcome.tsx @@ -25,7 +25,7 @@ import { getDiagnostics } from "../wxApi"; import { PageLink } from "../renderHtml"; import { WalletDiagnostics } from "../../types/walletTypes"; -function Diagnostics() { +function Diagnostics(): JSX.Element { const [timedOut, setTimedOut] = useState(false); const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>( undefined, @@ -39,7 +39,7 @@ function Diagnostics() { setTimedOut(true); } }, 1000); - const doFetch = async () => { + const doFetch = async (): Promise<void> => { const d = await getDiagnostics(); console.log("got diagnostics", d); gotDiagnostics = true; @@ -95,7 +95,7 @@ function Diagnostics() { return <p>Running diagnostics ...</p>; } -function Welcome() { +function Welcome(): JSX.Element { return ( <> <p>Thank you for installing the wallet.</p> @@ -110,6 +110,6 @@ function Welcome() { ); } -export function createWelcomePage() { +export function createWelcomePage(): JSX.Element { return <Welcome />; }
\ No newline at end of file diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx index e071dc8ba..9020ddb0b 100644 --- a/src/webex/pages/withdraw.tsx +++ b/src/webex/pages/withdraw.tsx @@ -28,10 +28,9 @@ import { WithdrawDetails } from "../../types/walletTypes"; import { WithdrawDetailView, renderAmount } from "../renderHtml"; import React, { useState, useEffect } from "react"; -import * as ReactDOM from "react-dom"; import { getWithdrawDetails, acceptWithdrawal } from "../wxApi"; -function NewExchangeSelection(props: { talerWithdrawUri: string }) { +function NewExchangeSelection(props: { talerWithdrawUri: string }): JSX.Element { const [details, setDetails] = useState<WithdrawDetails | undefined>(); const [selectedExchange, setSelectedExchange] = useState< string | undefined @@ -43,7 +42,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) { const [errMsg, setErrMsg] = useState<string | undefined>(""); useEffect(() => { - const fetchData = async () => { + const fetchData = async (): Promise<void> => { console.log("getting from", talerWithdrawUri); let d: WithdrawDetails | undefined = undefined; try { @@ -145,7 +144,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) { ); } - const accept = async () => { + const accept = async (): Promise<void> => { console.log("accepting exchange", selectedExchange); const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange!); console.log("accept withdrawal response", res); @@ -197,27 +196,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) { ); } -async function main() { - try { - const url = new URL(document.location.href); - const talerWithdrawUri = url.searchParams.get("talerWithdrawUri"); - if (!talerWithdrawUri) { - throw Error("withdraw URI required"); - } - - ReactDOM.render( - <NewExchangeSelection talerWithdrawUri={talerWithdrawUri} />, - 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 = i18n.str`Fatal error: "${e.message}".`; - console.error("got error", e); - } -} - -export function createWithdrawPage() { +export function createWithdrawPage(): JSX.Element { const url = new URL(document.location.href); const talerWithdrawUri = url.searchParams.get("talerWithdrawUri"); if (!talerWithdrawUri) { diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx index b6ff1248c..8fc6a6a63 100644 --- a/src/webex/renderHtml.tsx +++ b/src/webex/renderHtml.tsx @@ -29,14 +29,13 @@ import { DenominationRecord } from "../types/dbTypes"; import { ExchangeWithdrawDetails } from "../types/walletTypes"; import * as i18n from "./i18n"; import React from "react"; -import ReactDOM from "react-dom"; import { stringifyTimestamp } from "../util/time"; /** * Render amount as HTML, which non-breaking space between * decimal value and currency. */ -export function renderAmount(amount: AmountJson | string) { +export function renderAmount(amount: AmountJson | string): JSX.Element { let a; if (typeof amount === "string") { a = Amounts.parse(amount); @@ -54,14 +53,17 @@ export function renderAmount(amount: AmountJson | string) { ); } -export const AmountView = ({ amount }: { amount: AmountJson | string }) => - renderAmount(amount); +export const AmountView = ({ + amount, +}: { + amount: AmountJson | string; +}): JSX.Element => renderAmount(amount); /** * Abbreviate a string to a given length, and show the full * string on hover as a tooltip. */ -export function abbrev(s: string, n = 5) { +export function abbrev(s: string, n = 5): JSX.Element { let sAbbrev = s; if (s.length > n) { sAbbrev = s.slice(0, n) + ".."; @@ -94,12 +96,12 @@ export class Collapsible extends React.Component< super(props); this.state = { collapsed: props.initiallyCollapsed }; } - render() { - const doOpen = (e: any) => { + render(): JSX.Element { + const doOpen = (e: any): void => { this.setState({ collapsed: false }); e.preventDefault(); }; - const doClose = (e: any) => { + const doClose = (e: any): void => { this.setState({ collapsed: true }); e.preventDefault(); }; @@ -188,7 +190,7 @@ function FeeDetailsView(props: { countByPub[x.denomPub] = c; }); - function row(denom: DenominationRecord) { + function row(denom: DenominationRecord): JSX.Element { return ( <tr> <td>{countByPub[denom.denomPub] + "x"}</td> @@ -296,7 +298,7 @@ interface ExpanderTextProps { /** * Show a heading with a toggle to show/hide the expandable content. */ -export function ExpanderText({ text }: ExpanderTextProps) { +export function ExpanderText({ text }: ExpanderTextProps): JSX.Element { return <span>{text}</span>; } @@ -310,7 +312,7 @@ export function ProgressButton( React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement >, -) { +): JSX.Element { return ( <button className="pure-button pure-button-primary" @@ -330,7 +332,9 @@ export function ProgressButton( ); } -export function PageLink(props: React.PropsWithChildren<{ pageName: string }>) { +export function PageLink( + props: React.PropsWithChildren<{ pageName: string }>, +): JSX.Element { const url = chrome.extension.getURL(`/src/webex/pages/${props.pageName}`); return ( <a className="actionLink" href={url} target="_blank"> diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts index a530b7506..07b223c87 100644 --- a/src/webex/wxApi.ts +++ b/src/webex/wxApi.ts @@ -38,6 +38,9 @@ import { WalletBalance, PurchaseDetails, WalletDiagnostics, + WithdrawDetails, + PreparePayResult, + AcceptWithdrawalResponse, } from "../types/walletTypes"; import { MessageMap, MessageType } from "./messages"; @@ -271,7 +274,7 @@ export function applyRefund(refundUrl: string): Promise<string> { /** * Abort a failed payment and try to get a refund. */ -export function abortFailedPayment(contractTermsHash: string) { +export function abortFailedPayment(contractTermsHash: string): Promise<void> { return callBackend("abort-failed-payment", { contractTermsHash }); } @@ -288,7 +291,7 @@ export function benchmarkCrypto(repetitions: number): Promise<BenchmarkResult> { export function getWithdrawDetails( talerWithdrawUri: string, maybeSelectedExchange: string | undefined, -) { +): Promise<WithdrawDetails> { return callBackend("get-withdraw-details", { talerWithdrawUri, maybeSelectedExchange, @@ -298,7 +301,7 @@ export function getWithdrawDetails( /** * Get details about a pay operation. */ -export function preparePay(talerPayUri: string) { +export function preparePay(talerPayUri: string): Promise<PreparePayResult> { return callBackend("prepare-pay", { talerPayUri }); } @@ -308,7 +311,7 @@ export function preparePay(talerPayUri: string) { export function acceptWithdrawal( talerWithdrawUri: string, selectedExchange: string, -) { +): Promise<AcceptWithdrawalResponse> { return callBackend("accept-withdrawal", { talerWithdrawUri, selectedExchange, diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts index ef4715dce..f26c14d37 100644 --- a/src/webex/wxBackend.ts +++ b/src/webex/wxBackend.ts @@ -40,7 +40,6 @@ import { BrowserHttpLib } from "../util/http"; import { OpenedPromise, openPromise } from "../util/promiseUtils"; import { classifyTalerUri, TalerUriType } from "../util/taleruri"; import { Wallet } from "../wallet"; -import { ChromeBadge } from "./chromeBadge"; import { isFirefox } from "./compat"; import { MessageType } from "./messages"; import * as wxApi from "./wxApi"; @@ -49,6 +48,21 @@ import { Database } from "../util/query"; const NeedsWallet = Symbol("NeedsWallet"); +/** + * Currently active wallet instance. Might be unloaded and + * re-instantiated when the database is reset. + */ +let currentWallet: Wallet | undefined; + +let currentDatabase: IDBDatabase | undefined; + +/** + * Last version if an outdated DB, if applicable. + */ +let outdatedDbVersion: number | undefined; + +const walletInit: OpenedPromise<void> = openPromise<void>(); + async function handleMessage( sender: MessageSender, type: MessageType, @@ -57,14 +71,12 @@ async function handleMessage( function assertNotFound(t: never): never { console.error(`Request type ${t as string} unknown`); console.error(`Request detail was ${detail}`); - return ( - { - error: { - message: `request type ${t as string} unknown`, - requestType: type, - }, - } as never - ); + return { + error: { + message: `request type ${t as string} unknown`, + requestType: type, + }, + } as never; } function needsWallet(): Wallet { if (!currentWallet) { @@ -320,7 +332,7 @@ function getTab(tabId: number): Promise<chrome.tabs.Tab> { }); } -function setBadgeText(options: chrome.browserAction.BadgeTextDetails) { +function setBadgeText(options: chrome.browserAction.BadgeTextDetails): void { // not supported by all browsers ... if (chrome && chrome.browserAction && chrome.browserAction.setBadgeText) { chrome.browserAction.setBadgeText(options); @@ -331,9 +343,12 @@ function setBadgeText(options: chrome.browserAction.BadgeTextDetails) { function waitMs(timeoutMs: number): Promise<void> { return new Promise((resolve, reject) => { - chrome.extension - .getBackgroundPage()! - .setTimeout(() => resolve(), timeoutMs); + const bgPage = chrome.extension.getBackgroundPage(); + if (!bgPage) { + reject("fatal: no background page"); + return; + } + bgPage.setTimeout(() => resolve(), timeoutMs); }); } @@ -359,7 +374,7 @@ function makeSyncWalletRedirect( if (isFirefox()) { // Some platforms don't support the sync redirect (yet), so fall back to // async redirect after a timeout. - const doit = async () => { + const doit = async (): Promise<void> => { await waitMs(150); const tab = await getTab(tabId); if (tab.url === oldUrl) { @@ -371,29 +386,13 @@ function makeSyncWalletRedirect( return { redirectUrl: outerUrl.href }; } -/** - * Currently active wallet instance. Might be unloaded and - * re-instantiated when the database is reset. - */ -let currentWallet: Wallet | undefined; - -let currentDatabase: IDBDatabase | undefined; - -/** - * Last version if an outdated DB, if applicable. - */ -let outdatedDbVersion: number | undefined; - -const walletInit: OpenedPromise<void> = openPromise<void>(); - -async function reinitWallet() { +async function reinitWallet(): Promise<void> { if (currentWallet) { currentWallet.stop(); currentWallet = undefined; } currentDatabase = undefined; setBadgeText({ text: "" }); - const badge = new ChromeBadge(); try { currentDatabase = await openTalerDatabase(indexedDB, reinitWallet); } catch (e) { @@ -461,7 +460,7 @@ try { * * Sets up all event handlers and other machinery. */ -export async function wxMain() { +export async function wxMain(): Promise<void> { // Explicitly unload the extension page as soon as an update is available, // so the update gets installed as soon as possible. chrome.runtime.onUpdateAvailable.addListener((details) => { @@ -505,8 +504,13 @@ export async function wxMain() { chrome.tabs.onRemoved.addListener((tabId, changeInfo) => { const tt = tabTimers[tabId] || []; + const bgPage = chrome.extension.getBackgroundPage(); + if (!bgPage) { + console.error("background page unavailable"); + return; + } for (const t of tt) { - chrome.extension.getBackgroundPage()!.clearTimeout(t); + bgPage.clearTimeout(t); } }); chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { @@ -515,12 +519,7 @@ export async function wxMain() { } const timers: number[] = []; - const addRun = (dt: number) => { - const id = chrome.extension.getBackgroundPage()!.setTimeout(run, dt); - timers.push(id); - }; - - const run = () => { + const run = (): void => { timers.shift(); chrome.tabs.get(tabId, (tab) => { if (chrome.runtime.lastError) { @@ -538,10 +537,20 @@ export async function wxMain() { document.dispatchEvent(new Event("taler-probe-result")); } `; - injectScript(tab.id!, { code, runAt: "document_start" }, uri.href); + injectScript(tab.id, { code, runAt: "document_start" }, uri.href); }); }; + const addRun = (dt: number): void => { + const bgPage = chrome.extension.getBackgroundPage(); + if (!bgPage) { + console.error("no background page"); + return; + } + const id = bgPage.setTimeout(run, dt); + timers.push(id); + }; + addRun(0); addRun(50); addRun(300); |