From cbe325cb0f3e492e28ee148106c6c81e4db7d6bf Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 14 Aug 2020 00:13:51 +0530 Subject: [PATCH] web extension WIP --- .../src/pageEntryPoint.ts | 3 + .../src/pages/popup.tsx | 8 +- .../src/pages/withdraw.tsx | 102 +------ .../taler-wallet-webextension/src/wxApi.ts | 61 ++-- .../src/wxBackend.ts | 280 +++--------------- .../static/benchmark.html | 6 +- .../taler-wallet-webextension/static/pay.html | 8 +- .../static/popup.html | 10 +- .../static/withdraw.html | 8 +- 9 files changed, 98 insertions(+), 388 deletions(-) diff --git a/packages/taler-wallet-webextension/src/pageEntryPoint.ts b/packages/taler-wallet-webextension/src/pageEntryPoint.ts index 9fd1d36f1..216cb83c3 100644 --- a/packages/taler-wallet-webextension/src/pageEntryPoint.ts +++ b/packages/taler-wallet-webextension/src/pageEntryPoint.ts @@ -26,6 +26,7 @@ import { createWithdrawPage } from "./pages/withdraw"; import { createWelcomePage } from "./pages/welcome"; import { createPayPage } from "./pages/pay"; import { createRefundPage } from "./pages/refund"; +import { setupI18n } from "taler-wallet-core"; function main(): void { try { @@ -65,6 +66,8 @@ function main(): void { } } +setupI18n("en-US"); + if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", main); } else { diff --git a/packages/taler-wallet-webextension/src/pages/popup.tsx b/packages/taler-wallet-webextension/src/pages/popup.tsx index 61edcfe50..e833f5950 100644 --- a/packages/taler-wallet-webextension/src/pages/popup.tsx +++ b/packages/taler-wallet-webextension/src/pages/popup.tsx @@ -397,19 +397,19 @@ function actionForTalerUri(talerUri: string): string | undefined { const uriType = classifyTalerUri(talerUri); switch (uriType) { case TalerUriType.TalerWithdraw: - return makeExtensionUrlWithParams("withdraw.html", { + return makeExtensionUrlWithParams("static/withdraw.html", { talerWithdrawUri: talerUri, }); case TalerUriType.TalerPay: - return makeExtensionUrlWithParams("pay.html", { + return makeExtensionUrlWithParams("static/pay.html", { talerPayUri: talerUri, }); case TalerUriType.TalerTip: - return makeExtensionUrlWithParams("tip.html", { + return makeExtensionUrlWithParams("static/tip.html", { talerTipUri: talerUri, }); case TalerUriType.TalerRefund: - return makeExtensionUrlWithParams("refund.html", { + return makeExtensionUrlWithParams("static/refund.html", { talerRefundUri: talerUri, }); case TalerUriType.TalerNotifyReserve: diff --git a/packages/taler-wallet-webextension/src/pages/withdraw.tsx b/packages/taler-wallet-webextension/src/pages/withdraw.tsx index 4a92704b3..1637176a9 100644 --- a/packages/taler-wallet-webextension/src/pages/withdraw.tsx +++ b/packages/taler-wallet-webextension/src/pages/withdraw.tsx @@ -29,11 +29,13 @@ import React, { useState, useEffect } from "react"; import { acceptWithdrawal, onUpdateNotification, + getWithdrawalDetailsForUri, } from "../wxApi"; +import { WithdrawUriInfoResponse } from "taler-wallet-core"; function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element { const [details, setDetails] = useState< - any | undefined + WithdrawUriInfoResponse | undefined >(); const [selectedExchange, setSelectedExchange] = useState< string | undefined @@ -54,55 +56,12 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element { useEffect(() => { const fetchData = async (): Promise => { - // FIXME: re-implement with new API - // console.log("getting from", talerWithdrawUri); - // let d: WithdrawalDetailsResponse | undefined = undefined; - // try { - // d = await getWithdrawDetails(talerWithdrawUri, selectedExchange); - // } catch (e) { - // console.error( - // `error getting withdraw details for uri ${talerWithdrawUri}, exchange ${selectedExchange}`, - // e, - // ); - // setErrMsg(e.message); - // return; - // } - // console.log("got withdrawDetails", d); - // if (!selectedExchange && d.bankWithdrawDetails.suggestedExchange) { - // console.log("setting selected exchange"); - // setSelectedExchange(d.bankWithdrawDetails.suggestedExchange); - // } - // setDetails(d); + const res = await getWithdrawalDetailsForUri({talerWithdrawUri: props.talerWithdrawUri}); + setDetails(res); }; fetchData(); }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]); - if (errMsg) { - return ( -
- - Could not get details for withdraw operation: - -

{errMsg}

-

- { - setSelecting(true); - setErrMsg(undefined); - setSelectedExchange(undefined); - setDetails(undefined); - }} - > - {i18n.str`Chose different exchange provider`} - -

-
- ); - } - if (!details) { return Loading...; } @@ -111,51 +70,6 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element { return Withdraw operation has been cancelled.; } - if (selecting) { - const bankSuggestion = - details && details.bankWithdrawDetails.suggestedExchange; - return ( -
- {i18n.str`Please select an exchange. You can review the details before after your selection.`} - {bankSuggestion && ( -
-

Bank Suggestion

- -
- )} -

Custom Selection

-

- setCustomUrl(e.target.value)} - value={customUrl} - /> -

- -
- ); - } - const accept = async (): Promise => { if (!selectedExchange) { throw Error("can't accept, no exchange selected"); @@ -173,7 +87,7 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {

Digital Cash Withdrawal

You are about to withdraw{" "} - {renderAmount(details.bankWithdrawDetails.amount)} from + {renderAmount(details.amount)} from your bank account into your wallet. {selectedExchange ? ( @@ -211,9 +125,9 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {

- {details.exchangeWithdrawDetails ? ( + {/* {details.exchangeWithdrawDetails ? ( - ) : null} + ) : null} */} ); diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 1bcee1285..836466ff3 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -21,14 +21,26 @@ /** * Imports. */ -import { AmountJson, ConfirmPayResult, BalancesResponse, PurchaseDetails, TipStatus, BenchmarkResult, PreparePayResult, AcceptWithdrawalResponse, WalletDiagnostics } from "taler-wallet-core"; - +import { + AmountJson, + ConfirmPayResult, + BalancesResponse, + PurchaseDetails, + TipStatus, + BenchmarkResult, + PreparePayResult, + AcceptWithdrawalResponse, + WalletDiagnostics, + CoreApiResponse, + OperationFailedError, + GetWithdrawalDetailsForUriRequest, + WithdrawUriInfoResponse, +} from "taler-wallet-core"; export interface ExtendedPermissionsResponse { newValue: boolean; } - /** * Response with information about available version upgrades. */ @@ -50,23 +62,9 @@ export interface UpgradeResponse { oldDbVersion: string; } -/** - * Error thrown when the function from the backend (via RPC) threw an error. - */ -export class WalletApiError extends Error { - constructor(message: string, public detail: any) { - super(message); - // restore prototype chain - Object.setPrototypeOf(this, new.target.prototype); - } -} - -async function callBackend( - type: string, - detail: any, -): Promise { +async function callBackend(operation: string, payload: any): Promise { return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ type, detail }, (resp) => { + chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => { if (chrome.runtime.lastError) { console.log("Error calling backend"); reject( @@ -75,19 +73,17 @@ async function callBackend( ), ); } - if (typeof resp === "object" && resp && resp.error) { - console.warn("response error:", resp); - const e = new WalletApiError(resp.error.message, resp.error); - reject(e); - } else { - resolve(resp); + console.log("got response", resp); + const r = resp as CoreApiResponse; + if (r.type === "error") { + reject(new OperationFailedError(r.error)); + return; } + resolve(r.result); }); }); } - - /** * Start refreshing a coin. */ @@ -123,7 +119,7 @@ export function resetDb(): Promise { * Get balances for all currencies/exchanges. */ export function getBalance(): Promise { - return callBackend("balances", {}); + return callBackend("getBalances", {}); } /** @@ -227,6 +223,15 @@ export function getExtendedPermissions(): Promise { return callBackend("get-extended-permissions", {}); } +/** + * Get diagnostics information + */ +export function getWithdrawalDetailsForUri( + req: GetWithdrawalDetailsForUriRequest, +): Promise { + return callBackend("getWithdrawalDetailsForUri", req); +} + export function onUpdateNotification(f: () => void): () => void { const port = chrome.runtime.connect({ name: "notifications" }); const listener = (): void => { diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index ce024e096..86008dd99 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -28,7 +28,21 @@ import * as wxApi from "./wxApi"; import MessageSender = chrome.runtime.MessageSender; import { extendedPermissions } from "./permissions"; -import { Wallet, OpenedPromise, openPromise, deleteTalerDatabase, WALLET_DB_MINOR_VERSION, WalletDiagnostics, openTalerDatabase, Database, classifyTalerUri, TalerUriType } from "taler-wallet-core"; +import { + Wallet, + OpenedPromise, + openPromise, + deleteTalerDatabase, + WALLET_DB_MINOR_VERSION, + WalletDiagnostics, + openTalerDatabase, + Database, + classifyTalerUri, + TalerUriType, + makeErrorDetails, + TalerErrorCode, + handleCoreApiRequest, +} from "taler-wallet-core"; import { BrowserHttpLib } from "./browserHttpLib"; import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory"; @@ -51,254 +65,28 @@ const walletInit: OpenedPromise = openPromise(); const notificationPorts: chrome.runtime.Port[] = []; -async function handleMessage( - sender: MessageSender, - type: string, - detail: any, -): Promise { - function needsWallet(): Wallet { - if (!currentWallet) { - throw NeedsWallet; - } - return currentWallet; - } - switch (type) { - case "balances": { - return needsWallet().getBalances(); - } - case "dump-db": { - const db = needsWallet().db; - return db.exportDatabase(); - } - case "import-db": { - const db = needsWallet().db; - return db.importDatabase(detail.dump); - } - case "ping": { - return Promise.resolve(); - } - case "reset-db": { - deleteTalerDatabase(indexedDB); - setBadgeText({ text: "" }); - console.log("reset done"); - if (!currentWallet) { - reinitWallet(); - } - return Promise.resolve({}); - } - case "confirm-pay": { - if (typeof detail.proposalId !== "string") { - throw Error("proposalId must be string"); - } - return needsWallet().confirmPay(detail.proposalId, detail.sessionId); - } - case "exchange-info": { - if (!detail.baseUrl) { - return Promise.resolve({ error: "bad url" }); - } - return needsWallet().updateExchangeFromUrl(detail.baseUrl); - } - case "get-exchanges": { - return needsWallet().getExchangeRecords(); - } - case "get-currencies": { - return needsWallet().getCurrencies(); - } - case "update-currency": { - return needsWallet().updateCurrency(detail.currencyRecord); - } - case "get-reserves": { - if (typeof detail.exchangeBaseUrl !== "string") { - return Promise.reject(Error("exchangeBaseUrl missing")); - } - return needsWallet().getReserves(detail.exchangeBaseUrl); - } - case "get-coins": { - if (typeof detail.exchangeBaseUrl !== "string") { - return Promise.reject(Error("exchangBaseUrl missing")); - } - return needsWallet().getCoinsForExchange(detail.exchangeBaseUrl); - } - case "get-denoms": { - if (typeof detail.exchangeBaseUrl !== "string") { - return Promise.reject(Error("exchangBaseUrl missing")); - } - return needsWallet().getDenoms(detail.exchangeBaseUrl); - } - case "refresh-coin": { - if (typeof detail.coinPub !== "string") { - return Promise.reject(Error("coinPub missing")); - } - return needsWallet().refresh(detail.coinPub); - } - case "get-sender-wire-infos": { - return needsWallet().getSenderWireInfos(); - } - case "return-coins": { - const d = { - amount: detail.amount, - exchange: detail.exchange, - senderWire: detail.senderWire, - }; - return needsWallet().returnCoins(d); - } - case "check-upgrade": { - let dbResetRequired = false; - if (!currentWallet) { - dbResetRequired = true; - } - const resp: wxApi.UpgradeResponse = { - currentDbVersion: WALLET_DB_MINOR_VERSION.toString(), - dbResetRequired, - oldDbVersion: (outdatedDbVersion || "unknown").toString(), - }; - return resp; - } - case "get-purchase-details": { - const proposalId = detail.proposalId; - if (!proposalId) { - throw Error("proposalId missing"); - } - if (typeof proposalId !== "string") { - throw Error("proposalId must be a string"); - } - return needsWallet().getPurchaseDetails(proposalId); - } - case "accept-refund": - return needsWallet().applyRefund(detail.refundUrl); - case "get-tip-status": { - return needsWallet().getTipStatus(detail.talerTipUri); - } - case "accept-tip": { - return needsWallet().acceptTip(detail.talerTipUri); - } - case "abort-failed-payment": { - if (!detail.contractTermsHash) { - throw Error("contracTermsHash not given"); - } - return needsWallet().abortFailedPayment(detail.contractTermsHash); - } - case "benchmark-crypto": { - if (!detail.repetitions) { - throw Error("repetitions not given"); - } - return needsWallet().benchmarkCrypto(detail.repetitions); - } - case "accept-withdrawal": { - return needsWallet().acceptWithdrawal( - detail.talerWithdrawUri, - detail.selectedExchange, - ); - } - case "get-diagnostics": { - const manifestData = chrome.runtime.getManifest(); - const errors: string[] = []; - let firefoxIdbProblem = false; - let dbOutdated = false; - try { - await walletInit.promise; - } catch (e) { - errors.push("Error during wallet initialization: " + e); - if ( - currentDatabase === undefined && - outdatedDbVersion === undefined && - isFirefox() - ) { - firefoxIdbProblem = true; - } - } - if (!currentWallet) { - errors.push("Could not create wallet backend."); - } - if (!currentDatabase) { - errors.push("Could not open database"); - } - if (outdatedDbVersion !== undefined) { - errors.push(`Outdated DB version: ${outdatedDbVersion}`); - dbOutdated = true; - } - const diagnostics: WalletDiagnostics = { - walletManifestDisplayVersion: - manifestData.version_name || "(undefined)", - walletManifestVersion: manifestData.version, - errors, - firefoxIdbProblem, - dbOutdated, - }; - return diagnostics; - } - case "prepare-pay": - return needsWallet().preparePayForUri(detail.talerPayUri); - case "set-extended-permissions": { - const newVal = detail.value; - console.log("new extended permissions value", newVal); - if (newVal) { - setupHeaderListener(); - return { newValue: true }; - } else { - await new Promise((resolve, reject) => { - getPermissionsApi().remove(extendedPermissions, (rem) => { - console.log("permissions removed:", rem); - resolve(); - }); - }); - return { newVal: false }; - } - } - case "get-extended-permissions": { - const res = await new Promise((resolve, reject) => { - getPermissionsApi().contains(extendedPermissions, (result: boolean) => { - resolve(result); - }); - }); - return { newValue: res }; - } - default: - console.error(`Request type ${type} unknown`); - console.error(`Request detail was ${detail}`); - return { - error: { - message: `request type ${type} unknown`, - requestType: type, - }, - }; - } -} - async function dispatch( req: any, sender: any, sendResponse: any, ): Promise { + const w = currentWallet; + if (!w) { + sendResponse( + makeErrorDetails( + TalerErrorCode.WALLET_CORE_NOT_AVAILABLE, + "wallet core not available", + {}, + ), + ); + return; + } + + const r = await handleCoreApiRequest(w, req.operation, req.id, req.payload); try { - const p = handleMessage(sender, req.type, req.detail); - const r = await p; - try { - sendResponse(r); - } catch (e) { - // might fail if tab disconnected - } + sendResponse(r); } catch (e) { - console.log(`exception during wallet handler for '${req.type}'`); - console.log("request", req); - console.error(e); - let stack; - try { - stack = e.stack.toString(); - } catch (e) { - // might fail - } - try { - sendResponse({ - error: { - message: e.message, - stack, - }, - }); - } catch (e) { - console.log(e); - // might fail if tab disconnected - } + // might fail if tab disconnected } } @@ -436,7 +224,7 @@ function headerListener( switch (uriType) { case TalerUriType.TalerWithdraw: return makeSyncWalletRedirect( - "withdraw.html", + "/static/withdraw.html", details.tabId, details.url, { @@ -445,7 +233,7 @@ function headerListener( ); case TalerUriType.TalerPay: return makeSyncWalletRedirect( - "pay.html", + "/static/pay.html", details.tabId, details.url, { @@ -454,7 +242,7 @@ function headerListener( ); case TalerUriType.TalerTip: return makeSyncWalletRedirect( - "tip.html", + "/static/tip.html", details.tabId, details.url, { @@ -463,7 +251,7 @@ function headerListener( ); case TalerUriType.TalerRefund: return makeSyncWalletRedirect( - "refund.html", + "/static/refund.html", details.tabId, details.url, { diff --git a/packages/taler-wallet-webextension/static/benchmark.html b/packages/taler-wallet-webextension/static/benchmark.html index a29fe0725..d0ca32aeb 100644 --- a/packages/taler-wallet-webextension/static/benchmark.html +++ b/packages/taler-wallet-webextension/static/benchmark.html @@ -3,9 +3,9 @@ Taler Wallet: Benchmarks - - - + + +
diff --git a/packages/taler-wallet-webextension/static/pay.html b/packages/taler-wallet-webextension/static/pay.html index 452c56df0..129765815 100644 --- a/packages/taler-wallet-webextension/static/pay.html +++ b/packages/taler-wallet-webextension/static/pay.html @@ -4,10 +4,10 @@ Taler Wallet: Confirm Contract - - - - + + + +