diff options
28 files changed, 1087 insertions, 529 deletions
diff --git a/packages/taler-wallet-webextension/dev-html/.gitignore b/packages/taler-wallet-webextension/dev-html/.gitignore new file mode 100644 index 000000000..c4f051f4f --- /dev/null +++ b/packages/taler-wallet-webextension/dev-html/.gitignore @@ -0,0 +1,4 @@ +/mocha.css +/mocha.js +/mocha.js.map +/manifest.json diff --git a/packages/taler-wallet-webextension/dev-html/index.html b/packages/taler-wallet-webextension/dev-html/index.html new file mode 100644 index 000000000..c0919e9b9 --- /dev/null +++ b/packages/taler-wallet-webextension/dev-html/index.html @@ -0,0 +1,48 @@ +<html> + <head> + <meta charset="utf-8" /> + <link rel="manifest" href="./manifest.json" /> + </head> + <body> + <script> + function openPopup() { + window.frames["popup"].location = "/popup.html"; + } + function openWallet() { + window.frames["wallet"].location = "/wallet.html"; + } + function closeWallet() { + window.frames["wallet"].location = "about:blank"; + } + </script> + <input type="text" /> + <button value="asd" onclick="openPopup()">open popup</button> + <button value="asd" onclick="closeWallet();openWallet()"> + reload wallet page + </button> + <hr /> + <iframe + id="popup-window" + name="popup" + src="about:blank" + name="popup" + width="500" + height="325" + > + </iframe> + <hr /> + <iframe + id="wallet-window" + name="wallet" + src="about:blank" + name="wallet" + width="800" + height="100%" + > + </iframe> + <hr /> + <iframe src="/tests.html" name="wallet" width="800" height="100%"> </iframe> + <hr /> + <script src="/dist/background.dev.js"></script> + </body> +</html> diff --git a/packages/taler-wallet-webextension/dev-html/popup.html b/packages/taler-wallet-webextension/dev-html/popup.html new file mode 100644 index 000000000..93a886d54 --- /dev/null +++ b/packages/taler-wallet-webextension/dev-html/popup.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <style> + html { + font-family: sans-serif; /* 1 */ + } + body { + margin: 0; + } + </style> + <style> + html { + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + overflow: hidden; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + } + </style> + + <link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" /> + <script src="/dist/popupEntryPoint.dev.js"></script> + </head> + + <body> + <taler-popup id="container" class="popup-container"></taler-popup> + </body> +</html> diff --git a/packages/taler-wallet-webextension/run-test-in-browser.html b/packages/taler-wallet-webextension/dev-html/tests.html index b027c0716..4b3ec93b5 100644 --- a/packages/taler-wallet-webextension/run-test-in-browser.html +++ b/packages/taler-wallet-webextension/dev-html/tests.html @@ -2,19 +2,18 @@ <html> <head> <title>Mocha Tests</title> - <link rel="stylesheet" href="node_modules/mocha/mocha.css" /> + <link rel="stylesheet" href="/mocha.css" /> </head> <body> <div id="mocha"></div> - <script src="node_modules/mocha/mocha.js"></script> + <script src="/mocha.js"></script> <script> mocha.setup("bdd"); </script> <!-- load code you want to test here --> - - <script src="dist/stories.test.js"></script> - <script src="dist/hooks/useTalerActionURL.test.js"></script> + <script src="/dist/stories.test.js"></script> + <script src="/dist/hooks/useTalerActionURL.test.js"></script> <!-- load your test files here --> <script> diff --git a/packages/taler-wallet-webextension/dev-html/wallet.html b/packages/taler-wallet-webextension/dev-html/wallet.html new file mode 100644 index 000000000..ff8616847 --- /dev/null +++ b/packages/taler-wallet-webextension/dev-html/wallet.html @@ -0,0 +1,29 @@ +<html> + <head> + <meta charset="utf-8" /> + <link rel="stylesheet" type="text/css" href="/dist/walletEntryPoint.css" /> + <style> + html { + font-family: sans-serif; /* 1 */ + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + } + </style> + <script src="/dist/walletEntryPoint.dev.js"></script> + </head> + + <body> + <div id="container" class="wallet-container"></div> + </body> +</html> diff --git a/packages/taler-wallet-webextension/run-wallet-in-browser.html b/packages/taler-wallet-webextension/run-wallet-in-browser.html deleted file mode 100644 index d0ee34ac4..000000000 --- a/packages/taler-wallet-webextension/run-wallet-in-browser.html +++ /dev/null @@ -1,14 +0,0 @@ -<html> - <head> - <link rel="manifest" href="./manifest.json" /> - </head> - <body> - <iframe src="./static/popup.html" name="popup" width="500" height="400"> - algo - </iframe> - <hr /> - <iframe src="./static/wallet.html" name="wallet" width="800" height="100%"> - otroe - </iframe> - </body> -</html> diff --git a/packages/taler-wallet-webextension/serve-esbuild.mjs b/packages/taler-wallet-webextension/serve-esbuild.mjs new file mode 100644 index 000000000..0fa2723ae --- /dev/null +++ b/packages/taler-wallet-webextension/serve-esbuild.mjs @@ -0,0 +1,22 @@ + +import linaria from '@linaria/esbuild' +import esbuild from 'esbuild' +import { buildConfig } from "./build-fast-with-linaria.mjs" +import fs from 'fs'; + +fs.writeFileSync("dev-html/manifest.json", fs.readFileSync("manifest-v2.json")) +fs.writeFileSync("dev-html/mocha.css", fs.readFileSync("node_modules/mocha/mocha.css")) +fs.writeFileSync("dev-html/mocha.js", fs.readFileSync("node_modules/mocha/mocha.js")) +fs.writeFileSync("dev-html/mocha.js.map", fs.readFileSync("node_modules/mocha/mocha.js.map")) + +const server = await esbuild + .serve({ + servedir: 'dev-html', + }, { ...buildConfig, outdir: 'dev-html/dist' }) + .catch((e) => { + console.log(e) + process.exit(1) + }); + +console.log("ready!", server.port); + diff --git a/packages/taler-wallet-webextension/src/background.dev.ts b/packages/taler-wallet-webextension/src/background.dev.ts new file mode 100644 index 000000000..1493ec246 --- /dev/null +++ b/packages/taler-wallet-webextension/src/background.dev.ts @@ -0,0 +1,43 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Entry point for the background page. + * + * @author sebasjm + */ + +/** + * Imports. + */ +import { platform, setupPlatform } from "./platform/api"; +import devAPI from "./platform/dev" +import { wxMain } from "./wxBackend"; + +console.log("Wallet setup for Dev API") +setupPlatform(devAPI) + +try { + platform.registerOnInstalled(() => { + platform.openWalletPage("/welcome") + }) +} catch (e) { + console.error(e); +} + +platform.notifyWhenAppIsReady(() => { + wxMain(); +}) diff --git a/packages/taler-wallet-webextension/src/background.ts b/packages/taler-wallet-webextension/src/background.ts index 9c572c176..7cda6e9ef 100644 --- a/packages/taler-wallet-webextension/src/background.ts +++ b/packages/taler-wallet-webextension/src/background.ts @@ -30,8 +30,8 @@ import { wxMain } from "./wxBackend"; const isFirefox = typeof (window as any)['InstallTrigger'] !== 'undefined' -//FIXME: create different entry point for any platform instead of -//switching in runtime +// FIXME: create different entry point for any platform instead of +// switching in runtime if (isFirefox) { console.log("Wallet setup for Firefox API") setupPlatform(firefoxAPI) diff --git a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts index 4494b9af8..f14e03d75 100644 --- a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts +++ b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts @@ -32,7 +32,7 @@ const worker: Worker = self as any as Worker; async function handleRequest( operation: string, id: number, - args: string[], + req: unknown, ): Promise<void> { const impl = nativeCrypto; @@ -42,7 +42,7 @@ async function handleRequest( } try { - const result = await (impl as any)[operation](...args); + const result = await (impl as any)[operation](req); worker.postMessage({ result, id }); } catch (e) { logger.error("error during operation", e); @@ -51,9 +51,9 @@ async function handleRequest( } worker.onmessage = (msg: MessageEvent) => { - const args = msg.data.args; - if (!Array.isArray(args)) { - console.error("args must be array"); + const req = msg.data.req; + if (typeof req !== "object") { + console.error("request must be an object"); return; } const id = msg.data.id; @@ -67,7 +67,7 @@ worker.onmessage = (msg: MessageEvent) => { return; } - handleRequest(operation, id, args).catch((e) => { + handleRequest(operation, id, req).catch((e) => { console.error("error in browser worker", e); }); }; diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 13e3189fb..e1b4dd2ac 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -143,7 +143,7 @@ export const Middle = styled.div` export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>` height: 290px; width: 500px; - overflow-y: scroll; + overflow-y: visible; display: flex; flex-direction: column; justify-content: space-between; diff --git a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts index 66d710705..a35801741 100644 --- a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts @@ -40,41 +40,18 @@ async function handleExtendedPerm(isEnabled: boolean, onChange: (value: boolean) if (!isEnabled) { // We set permissions here, since apparently FF wants this to be done // as the result of an input event ... - const granted = await platform.getPermissionsApi().request(getReadRequestPermissions()); - console.log("permissions granted:", granted); - const lastError = platform.getLastError(); - if (lastError) { + let granted: boolean; + try { + granted = await platform.getPermissionsApi().request(getReadRequestPermissions()); + } catch (lastError) { console.error("error requesting permissions"); console.error(lastError); onChange(false); - return; + return } - // try { + console.log("permissions granted:", granted); const res = await wxApi.setExtendedPermissions(granted); onChange(res.newValue); - // } finally { - // return - // } - - // return new Promise<void>((res) => { - // platform.getPermissionsApi().request(getReadRequestPermissions(), async (granted: boolean) => { - // console.log("permissions granted:", granted); - // const lastError = getLastError() - // if (lastError) { - // console.error("error requesting permissions"); - // console.error(lastError); - // onChange(false); - // return; - // } - // try { - // const res = await wxApi.setExtendedPermissions(granted); - // onChange(res.newValue); - // } finally { - // res() - // } - - // }); - // }) } await wxApi.setExtendedPermissions(false).then(r => onChange(r.newValue)); return diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index 9b4e02ffb..112f9721c 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -38,7 +38,7 @@ export interface CrossBrowserPermissionsApi { request(p: Permissions): Promise<boolean>; remove(p: Permissions): Promise<boolean>; - addPermissionsListener(callback: (p: Permissions) => void): void; + addPermissionsListener(callback: (p: Permissions, lastError?: string) => void): void; } @@ -57,31 +57,132 @@ export interface WalletVersion { */ export interface PlatformAPI { /** + * FIXME: should not be needed + * * check if the platform is firefox */ isFirefox(): boolean; + /** - * + * Permission API for checking and add a listener */ getPermissionsApi(): CrossBrowserPermissionsApi; + + /** + * Backend API + * + * Register a callback to be called when the wallet is ready to start + * @param callback + */ notifyWhenAppIsReady(callback: () => void): void; - openWalletURIFromPopup(uriType: TalerUriType, talerUri: string): void; + + /** + * Popup API + * + * Used when an TalerURI is found and open up from the popup UI. + * Closes the popup and open the URI into the wallet UI. + * + * @param talerUri + */ + openWalletURIFromPopup(talerUri: string): void; + + /** + * Backend API + * + * Open a page into the wallet UI + * @param page + */ openWalletPage(page: string): void; + + /** + * Popup API + * + * Open a page into the wallet UI and closed the popup + * @param page + */ openWalletPageFromPopup(page: string): void; - setMessageToWalletBackground(operation: string, payload: any): Promise<CoreApiResponse>; - listenToWalletNotifications(listener: (m: any) => void): () => void; - sendMessageToAllChannels(message: MessageFromBackend): void; - registerAllIncomingConnections(): void; - registerOnNewMessage(onNewMessage: (message: any, sender: any, callback: any) => void): void; - registerReloadOnNewVersion(): void; + + /** + * Backend API + * + * When a tab has been detected to have a Taler action the background process + * can use this function to redirect the tab to the wallet UI + * + * @param tabId + * @param page + */ redirectTabToWalletPage(tabId: number, page: string): void; + + /** + * Get the wallet version from manifest + */ getWalletVersion(): WalletVersion; + + + /** + * Backend API + */ + registerAllIncomingConnections(): void; + /** + * Backend API + */ + registerReloadOnNewVersion(): void; + /** + * Backend API + */ registerTalerHeaderListener(onHeader: (tabId: number, url: string) => void): void; + /** + * Backend API + */ registerOnInstalled(callback: () => void): void; + + /** + * Backend API + * + * Check if background process run as service worker. This is used from the + * wallet use different http api and crypto worker. + */ useServiceWorkerAsBackgroundProcess(): boolean; - getLastError(): string | undefined; - searchForTalerLinks(): string | undefined; + + /** + * Popup API + * + * Read the current tab html and try to find any Taler URI or QR code present. + * + * @return Taler URI if found + */ findTalerUriInActiveTab(): Promise<string | undefined>; + + /** + * Used from the frontend to send commands to the wallet + * + * @param operation + * @param payload + * + * @return response from the backend + */ + sendMessageToWalletBackground(operation: string, payload: any): Promise<CoreApiResponse>; + + /** + * Used from the frontend to receive notifications about new information + * @param listener + * @return function to unsubscribe the listener + */ + listenToWalletBackground(listener: (message: MessageFromBackend) => void): () => void; + + /** + * Use by the wallet backend to receive operations from frontend (popup & wallet) + * and send a response back. + * + * @param onNewMessage + */ + listenToAllChannels(onNewMessage: (message: any, sender: any, sendResponse: (r: CoreApiResponse) => void) => void): void; + + /** + * Used by the wallet backend to send notification about new information + * @param message + */ + sendMessageToAllChannels(message: MessageFromBackend): void; } export let platform: PlatformAPI = undefined as any; diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts index dada23c57..91ccc220d 100644 --- a/packages/taler-wallet-webextension/src/platform/chrome.ts +++ b/packages/taler-wallet-webextension/src/platform/chrome.ts @@ -14,17 +14,16 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { TalerUriType } from "@gnu-taler/taler-util"; +import { classifyTalerUri, CoreApiResponse, TalerUriType } from "@gnu-taler/taler-util"; import { getReadRequestPermissions } from "../permissions"; import { CrossBrowserPermissionsApi, MessageFromBackend, Permissions, PlatformAPI } from "./api.js"; const api: PlatformAPI = { isFirefox, findTalerUriInActiveTab, - getLastError, getPermissionsApi, getWalletVersion, - listenToWalletNotifications, + listenToWalletBackground, notifyWhenAppIsReady, openWalletPage, openWalletPageFromPopup, @@ -32,12 +31,11 @@ const api: PlatformAPI = { redirectTabToWalletPage, registerAllIncomingConnections, registerOnInstalled, - registerOnNewMessage, + listenToAllChannels, registerReloadOnNewVersion, registerTalerHeaderListener, - searchForTalerLinks, sendMessageToAllChannels, - setMessageToWalletBackground, + sendMessageToWalletBackground, useServiceWorkerAsBackgroundProcess } @@ -50,7 +48,7 @@ function isFirefox(): boolean { export function contains(p: Permissions): Promise<boolean> { return new Promise((res, rej) => { chrome.permissions.contains(p, (resp) => { - const le = getLastError() + const le = chrome.runtime.lastError?.message if (le) { rej(le) } @@ -62,7 +60,7 @@ export function contains(p: Permissions): Promise<boolean> { export async function request(p: Permissions): Promise<boolean> { return new Promise((res, rej) => { chrome.permissions.request(p, (resp) => { - const le = getLastError() + const le = chrome.runtime.lastError?.message if (le) { rej(le) } @@ -74,7 +72,7 @@ export async function request(p: Permissions): Promise<boolean> { export async function remove(p: Permissions): Promise<boolean> { return new Promise((res, rej) => { chrome.permissions.remove(p, (resp) => { - const le = getLastError() + const le = chrome.runtime.lastError?.message if (le) { rej(le) } @@ -83,9 +81,12 @@ export async function remove(p: Permissions): Promise<boolean> { }) } -function addPermissionsListener(callback: (p: Permissions) => void): void { +function addPermissionsListener(callback: (p: Permissions, lastError?: string) => void): void { console.log("addPermissionListener is not supported for Firefox"); - chrome.permissions.onAdded.addListener(callback) + chrome.permissions.onAdded.addListener((perm: Permissions) => { + const lastError = chrome.runtime.lastError?.message; + callback(perm, lastError) + }) } function getPermissionsApi(): CrossBrowserPermissionsApi { @@ -107,7 +108,9 @@ function notifyWhenAppIsReady(callback: () => void) { } -function openWalletURIFromPopup(uriType: TalerUriType, talerUri: string) { +function openWalletURIFromPopup(talerUri: string) { + const uriType = classifyTalerUri(talerUri); + let url: string | undefined = undefined; switch (uriType) { case TalerUriType.TalerWithdraw: @@ -150,7 +153,7 @@ function openWalletPageFromPopup(page: string) { ); } -async function setMessageToWalletBackground(operation: string, payload: any): Promise<any> { +async function sendMessageToWalletBackground(operation: string, payload: any): Promise<any> { return new Promise<any>((resolve, reject) => { chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => { if (chrome.runtime.lastError) { @@ -164,7 +167,7 @@ async function setMessageToWalletBackground(operation: string, payload: any): Pr } let notificationPort: chrome.runtime.Port | undefined; -function listenToWalletNotifications(listener: (m: any) => void) { +function listenToWalletBackground(listener: (m: any) => void) { if (notificationPort === undefined) { notificationPort = chrome.runtime.connect({ name: "notifications" }) } @@ -203,7 +206,7 @@ function registerAllIncomingConnections() { }); } -function registerOnNewMessage(cb: (message: any, sender: any, callback: any) => void) { +function listenToAllChannels(cb: (message: any, sender: any, callback: (r: CoreApiResponse) => void) => void) { chrome.runtime.onMessage.addListener((m, s, c) => { cb(m, s, c) @@ -311,11 +314,6 @@ function useServiceWorkerAsBackgroundProcess() { return chrome.runtime.getManifest().manifest_version === 3 } -function getLastError() { - return chrome.runtime.lastError?.message; -} - - function searchForTalerLinks(): string | undefined { let found; found = document.querySelector("a[href^='taler://'") diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts new file mode 100644 index 000000000..a761bb014 --- /dev/null +++ b/packages/taler-wallet-webextension/src/platform/dev.ts @@ -0,0 +1,159 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { classifyTalerUri, CoreApiResponse, TalerUriType } from "@gnu-taler/taler-util"; +import { MessageFromBackend, PlatformAPI } from "./api"; + +const frames = ["popup", "wallet"] + +const api: PlatformAPI = ({ + isFirefox: () => false, + findTalerUriInActiveTab: async () => undefined, + getPermissionsApi: () => ({ + addPermissionsListener: () => undefined, contains: async () => true, remove: async () => false, request: async () => false + }), + getWalletVersion: () => ({ + version: 'none' + }), + notifyWhenAppIsReady: (fn: () => void) => { + let total = frames.length + function waitAndNotify() { + total-- + if (total < 1) { + console.log('done') + fn() + } + } + frames.forEach(f => { + const theFrame = window.frames[f as any] + if (theFrame.location.href === 'about:blank') { + waitAndNotify() + } else { + theFrame.addEventListener("load", waitAndNotify) + } + }) + }, + + openWalletPage: (page: string) => { + window.frames['wallet' as any].location = `/wallet.html#${page}` + }, + openWalletPageFromPopup: (page: string) => { + window.parent.frames['wallet' as any].location = `/wallet.html#${page}` + window.location.href = "about:blank" + }, + openWalletURIFromPopup: (page: string) => { + alert('openWalletURIFromPopup not implemented yet') + }, + redirectTabToWalletPage: (tabId: number, page: string) => { + alert('redirectTabToWalletPage not implemented yet') + }, + + registerAllIncomingConnections: () => undefined, + registerOnInstalled: (fn: () => void) => fn(), + registerReloadOnNewVersion: () => undefined, + registerTalerHeaderListener: () => undefined, + + useServiceWorkerAsBackgroundProcess: () => false, + + listenToAllChannels: (fn: (m: any, s: any, c: (r: CoreApiResponse) => void) => void) => { + window.addEventListener("message", (event: MessageEvent<IframeMessageType>) => { + if (event.data.type !== 'command') return + const sender = event.data.header.replyMe + fn(event.data.body, sender, (resp: CoreApiResponse) => { + if (event.source) { + const msg: IframeMessageResponse = { + type: "response", + header: { responseId: sender }, + body: resp + } + window.parent.postMessage(msg) + } + }) + }) + }, + sendMessageToAllChannels: (message: MessageFromBackend) => { + Array.from(window.frames).forEach(w => { + try { + w.postMessage({ + header: {}, body: message + }); + } catch (e) { + console.error(e) + } + }) + }, + listenToWalletBackground: (onNewMessage: (m: MessageFromBackend) => void) => { + function listener(event: MessageEvent<IframeMessageType>) { + if (event.data.type !== 'notification') return + onNewMessage(event.data.body) + } + window.parent.addEventListener("message", listener) + return () => { + window.parent.removeEventListener("message", listener) + } + }, + sendMessageToWalletBackground: async (operation: string, payload: any) => { + const replyMe = `reply-${Math.floor(Math.random() * 100000)}` + const message: IframeMessageCommand = { + type: 'command', + header: { replyMe }, + body: { operation, payload, id: "(none)" } + } + window.parent.postMessage(message) + + return new Promise((res, rej) => { + function listener(event: MessageEvent<IframeMessageType>) { + if (event.data.type !== "response" || event.data.header.responseId !== replyMe) { + return + } + res(event.data.body) + window.parent.removeEventListener("message", listener) + } + window.parent.addEventListener("message", listener, { + + }) + }) + + }, +}) + +type IframeMessageType = IframeMessageNotification | IframeMessageResponse | IframeMessageCommand; +interface IframeMessageNotification { + type: "notification"; + header: { + }, + body: MessageFromBackend +} +interface IframeMessageResponse { + type: "response"; + header: { + responseId: string; + }, + body: CoreApiResponse +} + +interface IframeMessageCommand { + type: "command"; + header: { + replyMe: string; + }, + body: { + operation: any, id: string, payload: any + } +} + +export default api; + diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx new file mode 100644 index 000000000..71b2d6c49 --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/Application.tsx @@ -0,0 +1,183 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Main entry point for extension pages. + * + * @author sebasjm + */ + +import { createHashHistory } from "history"; +import { Fragment, h, VNode } from "preact"; +import Router, { route, Route } from "preact-router"; +import { Match } from "preact-router/match"; +import { useEffect, useState } from "preact/hooks"; +import PendingTransactions from "../components/PendingTransactions"; +import { PopupBox } from "../components/styled"; +import { DevContextProvider } from "../context/devContext"; +import { IoCProviderForRuntime } from "../context/iocContext"; +import { + TranslationProvider, + useTranslationContext, +} from "../context/translation"; +import { useTalerActionURL } from "../hooks/useTalerActionURL"; +import { Pages, PopupNavBar } from "../NavigationBar"; +import { platform } from "../platform/api"; +import { BackupPage } from "../wallet/BackupPage"; +import { ProviderDetailPage } from "../wallet/ProviderDetailPage"; +import { BalancePage } from "./BalancePage"; +import { TalerActionFound } from "./TalerActionFound"; + +function CheckTalerActionComponent(): VNode { + const [talerActionUrl] = useTalerActionURL(); + + useEffect(() => { + if (talerActionUrl) + route(Pages.cta.replace(":action", encodeURIComponent(talerActionUrl))); + }, [talerActionUrl]); + + return <Fragment />; +} + +export function Application(): VNode { + const hash_history = createHashHistory(); + return ( + <TranslationProvider> + <DevContextProvider> + {({ devMode }: { devMode: boolean }) => ( + <IoCProviderForRuntime> + <PendingTransactions + goToTransaction={(txId: string) => + route(Pages.balance_transaction.replace(":tid", txId)) + } + /> + <Match> + {({ path }: { path: string }) => <PopupNavBar path={path} />} + </Match> + <CheckTalerActionComponent /> + <PopupBox devMode={devMode}> + <Router history={hash_history}> + <Route + path={Pages.balance} + component={BalancePage} + goToWalletManualWithdraw={() => + route( + Pages.balance_manual_withdraw.replace(":currency?", ""), + ) + } + goToWalletDeposit={(currency: string) => + route(Pages.balance_deposit.replace(":currency", currency)) + } + goToWalletHistory={(currency: string) => + route(Pages.balance_history.replace(":currency?", currency)) + } + /> + + <Route + path={Pages.cta} + component={function Action({ action }: { action: string }) { + const [, setDismissed] = useTalerActionURL(); + + return ( + <TalerActionFound + url={decodeURIComponent(action)} + onDismiss={() => { + setDismissed(true); + route(Pages.balance); + }} + /> + ); + }} + /> + + <Route + path={Pages.backup} + component={BackupPage} + onAddProvider={() => { + route(Pages.backup_provider_add); + }} + /> + <Route + path={Pages.backup_provider_detail} + component={ProviderDetailPage} + onBack={() => { + route(Pages.backup); + }} + /> + + <Route + path={Pages.balance_transaction} + component={RedirectToWalletPage} + /> + <Route + path={Pages.balance_manual_withdraw} + component={RedirectToWalletPage} + /> + <Route + path={Pages.balance_deposit} + component={RedirectToWalletPage} + /> + <Route + path={Pages.balance_history} + component={RedirectToWalletPage} + /> + <Route + path={Pages.backup_provider_add} + component={RedirectToWalletPage} + /> + <Route path={Pages.settings} component={RedirectToWalletPage} /> + <Route + path={Pages.settings_exchange_add} + component={RedirectToWalletPage} + /> + <Route path={Pages.dev} component={RedirectToWalletPage} /> + + <Route default component={Redirect} to={Pages.balance} /> + </Router> + </PopupBox> + </IoCProviderForRuntime> + )} + </DevContextProvider> + </TranslationProvider> + ); +} + +function RedirectToWalletPage(): VNode { + const page = (document.location.hash || "#/").replace("#", ""); + const [showText, setShowText] = useState(false); + useEffect(() => { + platform.openWalletPageFromPopup(page); + setTimeout(() => { + setShowText(true); + }, 250); + }); + const { i18n } = useTranslationContext(); + if (!showText) return <Fragment />; + return ( + <span> + <i18n.Translate> + this popup is being closed and you are being redirected to {page} + </i18n.Translate> + </span> + ); +} + +function Redirect({ to }: { to: string }): null { + useEffect(() => { + route(to, true); + }); + return null; +} diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx index a1082ad92..2af6526c2 100644 --- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx @@ -34,7 +34,7 @@ export function TalerActionFound({ url, onDismiss }: Props) { const uriType = classifyTalerUri(url); const { i18n } = useTranslationContext(); function redirectToWallet() { - platform.openWalletURIFromPopup(uriType, url); + platform.openWalletURIFromPopup(url); } return ( <Fragment> diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx new file mode 100644 index 000000000..551b47bfc --- /dev/null +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx @@ -0,0 +1,54 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Main entry point for extension pages. + * + * @author sebasjm + */ + +import { setupI18n } from "@gnu-taler/taler-util"; +import { Fragment, h, render } from "preact"; +import { strings } from "./i18n/strings"; +import { setupPlatform } from "./platform/api"; +import devAPI from "./platform/dev"; +import { Application } from "./popup/Application"; + +console.log("Wallet setup for Dev API"); +setupPlatform(devAPI); + +function main(): void { + try { + const container = document.getElementById("container"); + if (!container) { + throw Error("container not found, can't mount page contents"); + } + render(<Application />, container); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +setupI18n("en", strings); + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", main); +} else { + main(); +} diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index dfb12666b..d68918f7f 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -21,29 +21,23 @@ */ import { setupI18n } from "@gnu-taler/taler-util"; -import { createHashHistory } from "history"; -import { Fragment, h, render, VNode } from "preact"; -import Router, { route, Route } from "preact-router"; -import { Match } from "preact-router/match"; -import { useEffect, useState } from "preact/hooks"; -import PendingTransactions from "./components/PendingTransactions"; -import { PopupBox } from "./components/styled"; -import { DevContextProvider } from "./context/devContext"; -import { IoCProviderForRuntime } from "./context/iocContext"; -import { - TranslationProvider, - useTranslationContext, -} from "./context/translation"; -import { useTalerActionURL } from "./hooks/useTalerActionURL"; +import { Fragment, h, render } from "preact"; import { strings } from "./i18n/strings"; -import { Pages, PopupNavBar } from "./NavigationBar"; -import { platform, setupPlatform } from "./platform/api"; +import { setupPlatform } from "./platform/api"; import chromeAPI from "./platform/chrome"; import firefoxAPI from "./platform/firefox"; -import { BalancePage } from "./popup/BalancePage"; -import { TalerActionFound } from "./popup/TalerActionFound"; -import { BackupPage } from "./wallet/BackupPage"; -import { ProviderDetailPage } from "./wallet/ProviderDetailPage"; +import { Application } from "./popup/Application"; + +//FIXME: create different entry point for any platform instead of +//switching in runtime +const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined"; +if (isFirefox) { + console.log("Wallet setup for Firefox API"); + setupPlatform(firefoxAPI); +} else { + console.log("Wallet setup for Chrome API"); + setupPlatform(chromeAPI); +} function main(): void { try { @@ -62,160 +56,8 @@ function main(): void { setupI18n("en", strings); -//FIXME: create different entry point for any platform instead of -//switching in runtime -const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined"; -if (isFirefox) { - console.log("Wallet setup for Firefox API"); - setupPlatform(firefoxAPI); -} else { - console.log("Wallet setup for Chrome API"); - setupPlatform(chromeAPI); -} - if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", main); } else { main(); } - -function CheckTalerActionComponent(): VNode { - const [talerActionUrl] = useTalerActionURL(); - - useEffect(() => { - if (talerActionUrl) - route(Pages.cta.replace(":action", encodeURIComponent(talerActionUrl))); - }, [talerActionUrl]); - - return <Fragment />; -} - -function Application(): VNode { - const hash_history = createHashHistory(); - return ( - <TranslationProvider> - <DevContextProvider> - {({ devMode }: { devMode: boolean }) => ( - <IoCProviderForRuntime> - <PendingTransactions - goToTransaction={(txId: string) => - route(Pages.balance_transaction.replace(":tid", txId)) - } - /> - <Match> - {({ path }: { path: string }) => <PopupNavBar path={path} />} - </Match> - <CheckTalerActionComponent /> - <PopupBox devMode={devMode}> - <Router history={hash_history}> - <Route - path={Pages.balance} - component={BalancePage} - goToWalletManualWithdraw={() => - route( - Pages.balance_manual_withdraw.replace(":currency?", ""), - ) - } - goToWalletDeposit={(currency: string) => - route(Pages.balance_deposit.replace(":currency", currency)) - } - goToWalletHistory={(currency: string) => - route(Pages.balance_history.replace(":currency?", currency)) - } - /> - - <Route - path={Pages.cta} - component={function Action({ action }: { action: string }) { - const [, setDismissed] = useTalerActionURL(); - - return ( - <TalerActionFound - url={decodeURIComponent(action)} - onDismiss={() => { - setDismissed(true); - route(Pages.balance); - }} - /> - ); - }} - /> - - <Route - path={Pages.backup} - component={BackupPage} - onAddProvider={() => { - route(Pages.backup_provider_add); - }} - /> - <Route - path={Pages.backup_provider_detail} - component={ProviderDetailPage} - onBack={() => { - route(Pages.backup); - }} - /> - - <Route - path={Pages.balance_transaction} - component={RedirectToWalletPage} - /> - <Route - path={Pages.balance_manual_withdraw} - component={RedirectToWalletPage} - /> - <Route - path={Pages.balance_deposit} - component={RedirectToWalletPage} - /> - <Route - path={Pages.balance_history} - component={RedirectToWalletPage} - /> - <Route - path={Pages.backup_provider_add} - component={RedirectToWalletPage} - /> - <Route path={Pages.settings} component={RedirectToWalletPage} /> - <Route - path={Pages.settings_exchange_add} - component={RedirectToWalletPage} - /> - <Route path={Pages.dev} component={RedirectToWalletPage} /> - - <Route default component={Redirect} to={Pages.balance} /> - </Router> - </PopupBox> - </IoCProviderForRuntime> - )} - </DevContextProvider> - </TranslationProvider> - ); -} - -function RedirectToWalletPage(): VNode { - const page = (document.location.hash || "#/").replace("#", ""); - const [showText, setShowText] = useState(false); - useEffect(() => { - platform.openWalletPageFromPopup(page); - setTimeout(() => { - setShowText(true); - }, 250); - }); - const { i18n } = useTranslationContext(); - if (!showText) return <Fragment />; - return ( - <span> - <i18n.Translate> - this popup is being closed and you are being redirected to {page} - </i18n.Translate> - </span> - ); -} - -function Redirect({ to }: { to: string }): null { - useEffect(() => { - route(to, true); - }); - return null; -} diff --git a/packages/taler-wallet-webextension/src/svg/chevron-down.svg b/packages/taler-wallet-webextension/src/svg/chevron-down.svg index 36adbc1c6..0e1c0aeda 100644 --- a/packages/taler-wallet-webextension/src/svg/chevron-down.svg +++ b/packages/taler-wallet-webextension/src/svg/chevron-down.svg @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="92px" height="92px" viewBox="0 0 92 92" enable-background="new 0 0 92 92" xml:space="preserve"> -<path id="XMLID_467_" d="M46,63c-1.1,0-2.1-0.4-2.9-1.2l-25-26c-1.5-1.6-1.5-4.1,0.1-5.7c1.6-1.5,4.1-1.5,5.7,0.1l22.1,23l22.1-23 - c1.5-1.6,4.1-1.6,5.7-0.1c1.6,1.5,1.6,4.1,0.1,5.7l-25,26C48.1,62.6,47.1,63,46,63z"/> -</svg> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 92 92" enable-background="new 0 0 92 92" xml:space="preserve"> + <path id="XMLID_467_" d="M46,63c-1.1,0-2.1-0.4-2.9-1.2l-25-26c-1.5-1.6-1.5-4.1,0.1-5.7c1.6-1.5,4.1-1.5,5.7,0.1l22.1,23l22.1-23 + c1.5-1.6,4.1-1.6,5.7-0.1c1.6,1.5,1.6,4.1,0.1,5.7l-25,26C48.1,62.6,47.1,63,46,63z" /> +</svg>
\ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx index bebf036c9..751dbfba7 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx +++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx @@ -15,7 +15,7 @@ export function AddNewActionView({ onCancel }: Props): VNode { const { i18n } = useTranslationContext(); function redirectToWallet() { - platform.openWalletURIFromPopup(uriType, url); + platform.openWalletURIFromPopup(url); } return ( diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx new file mode 100644 index 000000000..2116b500e --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx @@ -0,0 +1,263 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Main entry point for extension pages. + * + * @author sebasjm + */ + +import { createHashHistory } from "history"; +import { Fragment, h, VNode } from "preact"; +import Router, { route, Route } from "preact-router"; +import Match from "preact-router/match"; +import { useEffect, useState } from "preact/hooks"; +import { LogoHeader } from "../components/LogoHeader"; +import PendingTransactions from "../components/PendingTransactions"; +import { SuccessBox, WalletBox } from "../components/styled"; +import { DevContextProvider } from "../context/devContext"; +import { IoCProviderForRuntime } from "../context/iocContext"; +import { + TranslationProvider, + useTranslationContext, +} from "../context/translation"; +import { PayPage } from "../cta/Pay"; +import { RefundPage } from "../cta/Refund"; +import { TipPage } from "../cta/Tip"; +import { WithdrawPage } from "../cta/Withdraw"; +import { Pages, WalletNavBar } from "../NavigationBar"; +import { DeveloperPage } from "../popup/DeveloperPage"; +import { BackupPage } from "./BackupPage"; +import { DepositPage } from "./DepositPage"; +import { ExchangeAddPage } from "./ExchangeAddPage"; +import { HistoryPage } from "./History"; +import { ManualWithdrawPage } from "./ManualWithdrawPage"; +import { ProviderAddPage } from "./ProviderAddPage"; +import { ProviderDetailPage } from "./ProviderDetailPage"; +import { SettingsPage } from "./Settings"; +import { TransactionPage } from "./Transaction"; +import { WelcomePage } from "./Welcome"; + +export function Application(): VNode { + const [globalNotification, setGlobalNotification] = useState< + VNode | undefined + >(undefined); + const hash_history = createHashHistory(); + function clearNotification(): void { + setGlobalNotification(undefined); + } + function clearNotificationWhenMovingOut(): void { + // const movingOutFromNotification = + // globalNotification && e.url !== globalNotification.to; + if (globalNotification) { + //&& movingOutFromNotification) { + setGlobalNotification(undefined); + } + } + const { i18n } = useTranslationContext(); + + return ( + <TranslationProvider> + <DevContextProvider> + <IoCProviderForRuntime> + {/* <Match/> won't work in the first render if <Router /> is not called first */} + {/* https://github.com/preactjs/preact-router/issues/415 */} + <Router history={hash_history} /> + <Match> + {({ path }: { path: string }) => { + if (path && path.startsWith("/cta")) return; + return ( + <Fragment> + <LogoHeader /> + <WalletNavBar path={path} /> + <div + style={{ + backgroundColor: "lightcyan", + display: "flex", + justifyContent: "center", + }} + > + <PendingTransactions + goToTransaction={(txId: string) => + route(Pages.balance_transaction.replace(":tid", txId)) + } + /> + </div> + </Fragment> + ); + }} + </Match> + <WalletBox> + {globalNotification && ( + <SuccessBox onClick={clearNotification}> + <div>{globalNotification}</div> + </SuccessBox> + )} + <Router + history={hash_history} + onChange={clearNotificationWhenMovingOut} + > + <Route path={Pages.welcome} component={WelcomePage} /> + + {/** + * BALANCE + */} + + <Route + path={Pages.balance_history} + component={HistoryPage} + goToWalletDeposit={(currency: string) => + route(Pages.balance_deposit.replace(":currency", currency)) + } + goToWalletManualWithdraw={(currency?: string) => + route( + Pages.balance_manual_withdraw.replace( + ":currency?", + currency || "", + ), + ) + } + /> + <Route + path={Pages.balance_transaction} + component={TransactionPage} + goToWalletHistory={(currency?: string) => { + route( + Pages.balance_history.replace(":currency?", currency || ""), + ); + }} + /> + + <Route + path={Pages.balance_manual_withdraw} + component={ManualWithdrawPage} + onCancel={() => { + route(Pages.balance); + }} + /> + + <Route + path={Pages.balance_deposit} + component={DepositPage} + onCancel={(currency: string) => { + route(Pages.balance_history.replace(":currency?", currency)); + }} + onSuccess={(currency: string) => { + route(Pages.balance_history.replace(":currency?", currency)); + setGlobalNotification( + <i18n.Translate> + All done, your transaction is in progress + </i18n.Translate>, + ); + }} + /> + {/** + * PENDING + */} + <Route path={Pages.settings} component={SettingsPage} /> + + {/** + * BACKUP + */} + <Route + path={Pages.backup} + component={BackupPage} + onAddProvider={() => { + route(Pages.backup_provider_add); + }} + /> + <Route + path={Pages.backup_provider_detail} + component={ProviderDetailPage} + onBack={() => { + route(Pages.backup); + }} + /> + <Route + path={Pages.backup_provider_add} + component={ProviderAddPage} + onBack={() => { + route(Pages.backup); + }} + /> + + {/** + * SETTINGS + */} + <Route + path={Pages.settings_exchange_add} + component={ExchangeAddPage} + onBack={() => { + route(Pages.balance); + }} + /> + + {/** + * DEV + */} + + <Route path={Pages.dev} component={DeveloperPage} /> + + {/** + * CALL TO ACTION + */} + <Route + path={Pages.cta_pay} + component={PayPage} + goToWalletManualWithdraw={(currency?: string) => + route( + Pages.balance_manual_withdraw.replace( + ":currency?", + currency || "", + ), + ) + } + goBack={() => route(Pages.balance)} + /> + <Route path={Pages.cta_refund} component={RefundPage} /> + <Route path={Pages.cta_tips} component={TipPage} /> + <Route path={Pages.cta_withdraw} component={WithdrawPage} /> + + {/** + * NOT FOUND + * all redirects should be at the end + */} + <Route + path={Pages.balance} + component={Redirect} + to={Pages.balance_history.replace(":currency?", "")} + /> + + <Route + default + component={Redirect} + to={Pages.balance_history.replace(":currency?", "")} + /> + </Router> + </WalletBox> + </IoCProviderForRuntime> + </DevContextProvider> + </TranslationProvider> + ); +} + +function Redirect({ to }: { to: string }): null { + useEffect(() => { + console.log("got some wrong route", to); + route(to, true); + }); + return null; +} diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx index b420c7ebb..df93b0c4e 100644 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx @@ -253,9 +253,6 @@ export function View({ There is no known bank account to send money to </i18n.Translate> </p> - <ButtonBoxWarning> - <i18n.Translate>Withdraw</i18n.Translate> - </ButtonBoxWarning> </WarningBox> <footer> <Button onClick={onCancel}> diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx new file mode 100644 index 000000000..ceceaf04c --- /dev/null +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx @@ -0,0 +1,54 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU 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. + + GNU 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 + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Main entry point for extension pages. + * + * @author sebasjm + */ + +import { setupI18n } from "@gnu-taler/taler-util"; +import { Fragment, h, render } from "preact"; +import { strings } from "./i18n/strings"; +import { setupPlatform } from "./platform/api"; +import devAPI from "./platform/dev"; +import { Application } from "./wallet/Application"; + +console.log("Wallet setup for Dev API"); +setupPlatform(devAPI); + +function main(): void { + try { + const container = document.getElementById("container"); + if (!container) { + throw Error("container not found, can't mount page contents"); + } + render(<Application />, container); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +setupI18n("en", strings); + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", main); +} else { + main(); +} diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index e7cb1f5e6..549eee507 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -21,40 +21,24 @@ */ import { setupI18n } from "@gnu-taler/taler-util"; -import { createHashHistory } from "history"; -import { Fragment, h, render, VNode } from "preact"; -import Router, { route, Route } from "preact-router"; -import Match from "preact-router/match"; -import { useEffect, useState } from "preact/hooks"; -import { LogoHeader } from "./components/LogoHeader"; -import PendingTransactions from "./components/PendingTransactions"; -import { SuccessBox, WalletBox } from "./components/styled"; -import { DevContextProvider } from "./context/devContext"; -import { IoCProviderForRuntime } from "./context/iocContext"; -import { - TranslationProvider, - useTranslationContext, -} from "./context/translation"; -import { PayPage } from "./cta/Pay"; -import { RefundPage } from "./cta/Refund"; -import { TipPage } from "./cta/Tip"; -import { WithdrawPage } from "./cta/Withdraw"; +import { Fragment, h, render } from "preact"; import { strings } from "./i18n/strings"; -import { Pages, WalletNavBar } from "./NavigationBar"; import { setupPlatform } from "./platform/api"; import chromeAPI from "./platform/chrome"; import firefoxAPI from "./platform/firefox"; -import { DeveloperPage } from "./popup/DeveloperPage"; -import { BackupPage } from "./wallet/BackupPage"; -import { DepositPage } from "./wallet/DepositPage"; -import { ExchangeAddPage } from "./wallet/ExchangeAddPage"; -import { HistoryPage } from "./wallet/History"; -import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage"; -import { ProviderAddPage } from "./wallet/ProviderAddPage"; -import { ProviderDetailPage } from "./wallet/ProviderDetailPage"; -import { SettingsPage } from "./wallet/Settings"; -import { TransactionPage } from "./wallet/Transaction"; -import { WelcomePage } from "./wallet/Welcome"; +import { Application } from "./wallet/Application"; + +const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined"; + +//FIXME: create different entry point for any platform instead of +//switching in runtime +if (isFirefox) { + console.log("Wallet setup for Firefox API"); + setupPlatform(firefoxAPI); +} else { + console.log("Wallet setup for Chrome API"); + setupPlatform(chromeAPI); +} function main(): void { try { @@ -73,230 +57,8 @@ function main(): void { setupI18n("en", strings); -const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined"; -//FIXME: create different entry point for any platform instead of -//switching in runtime -if (isFirefox) { - console.log("Wallet setup for Firefox API"); - setupPlatform(firefoxAPI); -} else { - console.log("Wallet setup for Chrome API"); - setupPlatform(chromeAPI); -} - if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", main); } else { main(); } - -function Application(): VNode { - const [globalNotification, setGlobalNotification] = useState< - VNode | undefined - >(undefined); - const hash_history = createHashHistory(); - function clearNotification(): void { - setGlobalNotification(undefined); - } - function clearNotificationWhenMovingOut(): void { - // const movingOutFromNotification = - // globalNotification && e.url !== globalNotification.to; - if (globalNotification) { - //&& movingOutFromNotification) { - setGlobalNotification(undefined); - } - } - const { i18n } = useTranslationContext(); - - return ( - <TranslationProvider> - <DevContextProvider> - <IoCProviderForRuntime> - {/* <Match/> won't work in the first render if <Router /> is not called first */} - {/* https://github.com/preactjs/preact-router/issues/415 */} - <Router history={hash_history} /> - <Match> - {({ path }: { path: string }) => { - if (path && path.startsWith("/cta")) return; - return ( - <Fragment> - <LogoHeader /> - <WalletNavBar path={path} /> - <div - style={{ - backgroundColor: "lightcyan", - display: "flex", - justifyContent: "center", - }} - > - <PendingTransactions - goToTransaction={(txId: string) => - route(Pages.balance_transaction.replace(":tid", txId)) - } - /> - </div> - </Fragment> - ); - }} - </Match> - <WalletBox> - {globalNotification && ( - <SuccessBox onClick={clearNotification}> - <div>{globalNotification}</div> - </SuccessBox> - )} - <Router - history={hash_history} - onChange={clearNotificationWhenMovingOut} - > - <Route path={Pages.welcome} component={WelcomePage} /> - - {/** - * BALANCE - */} - - <Route - path={Pages.balance_history} - component={HistoryPage} - goToWalletDeposit={(currency: string) => - route(Pages.balance_deposit.replace(":currency", currency)) - } - goToWalletManualWithdraw={(currency?: string) => - route( - Pages.balance_manual_withdraw.replace( - ":currency?", - currency || "", - ), - ) - } - /> - <Route - path={Pages.balance_transaction} - component={TransactionPage} - goToWalletHistory={(currency?: string) => { - route( - Pages.balance_history.replace(":currency?", currency || ""), - ); - }} - /> - - <Route - path={Pages.balance_manual_withdraw} - component={ManualWithdrawPage} - onCancel={() => { - route(Pages.balance); - }} - /> - - <Route - path={Pages.balance_deposit} - component={DepositPage} - onCancel={(currency: string) => { - route(Pages.balance_history.replace(":currency?", currency)); - }} - onSuccess={(currency: string) => { - route(Pages.balance_history.replace(":currency?", currency)); - setGlobalNotification( - <i18n.Translate> - All done, your transaction is in progress - </i18n.Translate>, - ); - }} - /> - {/** - * PENDING - */} - <Route path={Pages.settings} component={SettingsPage} /> - - {/** - * BACKUP - */} - <Route - path={Pages.backup} - component={BackupPage} - onAddProvider={() => { - route(Pages.backup_provider_add); - }} - /> - <Route - path={Pages.backup_provider_detail} - component={ProviderDetailPage} - onBack={() => { - route(Pages.backup); - }} - /> - <Route - path={Pages.backup_provider_add} - component={ProviderAddPage} - onBack={() => { - route(Pages.backup); - }} - /> - - {/** - * SETTINGS - */} - <Route - path={Pages.settings_exchange_add} - component={ExchangeAddPage} - onBack={() => { - route(Pages.balance); - }} - /> - - {/** - * DEV - */} - - <Route path={Pages.dev} component={DeveloperPage} /> - - {/** - * CALL TO ACTION - */} - <Route - path={Pages.cta_pay} - component={PayPage} - goToWalletManualWithdraw={(currency?: string) => - route( - Pages.balance_manual_withdraw.replace( - ":currency?", - currency || "", - ), - ) - } - goBack={() => route(Pages.balance)} - /> - <Route path={Pages.cta_refund} component={RefundPage} /> - <Route path={Pages.cta_tips} component={TipPage} /> - <Route path={Pages.cta_withdraw} component={WithdrawPage} /> - - {/** - * NOT FOUND - * all redirects should be at the end - */} - <Route - path={Pages.balance} - component={Redirect} - to={Pages.balance_history.replace(":currency?", "")} - /> - - <Route - default - component={Redirect} - to={Pages.balance_history.replace(":currency?", "")} - /> - </Router> - </WalletBox> - </IoCProviderForRuntime> - </DevContextProvider> - </TranslationProvider> - ); -} - -function Redirect({ to }: { to: string }): null { - useEffect(() => { - console.log("got some wrong route", to); - route(to, true); - }); - return null; -} diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index dd53a4feb..01db2fd91 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -97,7 +97,7 @@ export interface UpgradeResponse { async function callBackend(operation: string, payload: any): Promise<any> { let response: CoreApiResponse; try { - response = await platform.setMessageToWalletBackground(operation, payload); + response = await platform.sendMessageToWalletBackground(operation, payload); } catch (e) { console.log("Error calling backend"); throw new Error(`Error contacting backend: ${e}`); @@ -417,11 +417,11 @@ export function onUpdateNotification( messageTypes: Array<NotificationType>, doCallback: () => void, ): () => void { - const listener = (message: MessageFromBackend): void => { + const onNewMessage = (message: MessageFromBackend): void => { const shouldNotify = messageTypes.includes(message.type); if (shouldNotify) { doCallback(); } }; - return platform.listenToWalletNotifications(listener); + return platform.listenToWalletBackground(onNewMessage); } diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index 048c8fc79..6b73acd02 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -269,7 +269,7 @@ export async function wxMain(): Promise<void> { // Handlers for messages coming directly from the content // script on the page - platform.registerOnNewMessage((message, sender, callback) => { + platform.listenToAllChannels((message, sender, callback) => { afterWalletIsInitialized.then(() => { dispatch(message, sender, callback); }); @@ -285,8 +285,7 @@ export async function wxMain(): Promise<void> { // On platforms that support it, also listen to external // modification of permissions. - platform.getPermissionsApi().addPermissionsListener((perm) => { - const lastError = platform.getLastError() + platform.getPermissionsApi().addPermissionsListener((perm, lastError) => { if (lastError) { console.error(lastError); return; diff --git a/packages/taler-wallet-webextension/static/wallet.html b/packages/taler-wallet-webextension/static/wallet.html index f9dd8a19b..2c9c74b7f 100644 --- a/packages/taler-wallet-webextension/static/wallet.html +++ b/packages/taler-wallet-webextension/static/wallet.html @@ -2,7 +2,7 @@ <html> <head> <meta charset="utf-8" /> - <link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" /> + <link rel="stylesheet" type="text/css" href="/dist/walletEntryPoint.css" /> <link rel="icon" href="/static/img/icon.png" /> <script src="/dist/walletEntryPoint.js"></script> <style> |