new dev environment
This commit is contained in:
parent
00fb648269
commit
ddfb40e50c
4
packages/taler-wallet-webextension/dev-html/.gitignore
vendored
Normal file
4
packages/taler-wallet-webextension/dev-html/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/mocha.css
|
||||||
|
/mocha.js
|
||||||
|
/mocha.js.map
|
||||||
|
/manifest.json
|
48
packages/taler-wallet-webextension/dev-html/index.html
Normal file
48
packages/taler-wallet-webextension/dev-html/index.html
Normal file
@ -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>
|
39
packages/taler-wallet-webextension/dev-html/popup.html
Normal file
39
packages/taler-wallet-webextension/dev-html/popup.html
Normal file
@ -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>
|
@ -2,19 +2,18 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Mocha Tests</title>
|
<title>Mocha Tests</title>
|
||||||
<link rel="stylesheet" href="node_modules/mocha/mocha.css" />
|
<link rel="stylesheet" href="/mocha.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mocha"></div>
|
<div id="mocha"></div>
|
||||||
<script src="node_modules/mocha/mocha.js"></script>
|
<script src="/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
mocha.setup("bdd");
|
mocha.setup("bdd");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- load code you want to test here -->
|
<!-- load code you want to test here -->
|
||||||
|
<script src="/dist/stories.test.js"></script>
|
||||||
<script src="dist/stories.test.js"></script>
|
<script src="/dist/hooks/useTalerActionURL.test.js"></script>
|
||||||
<script src="dist/hooks/useTalerActionURL.test.js"></script>
|
|
||||||
<!-- load your test files here -->
|
<!-- load your test files here -->
|
||||||
|
|
||||||
<script>
|
<script>
|
29
packages/taler-wallet-webextension/dev-html/wallet.html
Normal file
29
packages/taler-wallet-webextension/dev-html/wallet.html
Normal file
@ -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>
|
@ -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>
|
|
22
packages/taler-wallet-webextension/serve-esbuild.mjs
Normal file
22
packages/taler-wallet-webextension/serve-esbuild.mjs
Normal file
@ -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);
|
||||||
|
|
43
packages/taler-wallet-webextension/src/background.dev.ts
Normal file
43
packages/taler-wallet-webextension/src/background.dev.ts
Normal file
@ -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();
|
||||||
|
})
|
@ -30,8 +30,8 @@ import { wxMain } from "./wxBackend";
|
|||||||
|
|
||||||
const isFirefox = typeof (window as any)['InstallTrigger'] !== 'undefined'
|
const isFirefox = typeof (window as any)['InstallTrigger'] !== 'undefined'
|
||||||
|
|
||||||
//FIXME: create different entry point for any platform instead of
|
// FIXME: create different entry point for any platform instead of
|
||||||
//switching in runtime
|
// switching in runtime
|
||||||
if (isFirefox) {
|
if (isFirefox) {
|
||||||
console.log("Wallet setup for Firefox API")
|
console.log("Wallet setup for Firefox API")
|
||||||
setupPlatform(firefoxAPI)
|
setupPlatform(firefoxAPI)
|
||||||
|
@ -32,7 +32,7 @@ const worker: Worker = self as any as Worker;
|
|||||||
async function handleRequest(
|
async function handleRequest(
|
||||||
operation: string,
|
operation: string,
|
||||||
id: number,
|
id: number,
|
||||||
args: string[],
|
req: unknown,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const impl = nativeCrypto;
|
const impl = nativeCrypto;
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ async function handleRequest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await (impl as any)[operation](...args);
|
const result = await (impl as any)[operation](req);
|
||||||
worker.postMessage({ result, id });
|
worker.postMessage({ result, id });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("error during operation", e);
|
logger.error("error during operation", e);
|
||||||
@ -51,9 +51,9 @@ async function handleRequest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
worker.onmessage = (msg: MessageEvent) => {
|
worker.onmessage = (msg: MessageEvent) => {
|
||||||
const args = msg.data.args;
|
const req = msg.data.req;
|
||||||
if (!Array.isArray(args)) {
|
if (typeof req !== "object") {
|
||||||
console.error("args must be array");
|
console.error("request must be an object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = msg.data.id;
|
const id = msg.data.id;
|
||||||
@ -67,7 +67,7 @@ worker.onmessage = (msg: MessageEvent) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRequest(operation, id, args).catch((e) => {
|
handleRequest(operation, id, req).catch((e) => {
|
||||||
console.error("error in browser worker", e);
|
console.error("error in browser worker", e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -143,7 +143,7 @@ export const Middle = styled.div`
|
|||||||
export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>`
|
export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>`
|
||||||
height: 290px;
|
height: 290px;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
overflow-y: scroll;
|
overflow-y: visible;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -40,41 +40,18 @@ async function handleExtendedPerm(isEnabled: boolean, onChange: (value: boolean)
|
|||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
// We set permissions here, since apparently FF wants this to be done
|
// We set permissions here, since apparently FF wants this to be done
|
||||||
// as the result of an input event ...
|
// as the result of an input event ...
|
||||||
const granted = await platform.getPermissionsApi().request(getReadRequestPermissions());
|
let granted: boolean;
|
||||||
console.log("permissions granted:", granted);
|
try {
|
||||||
const lastError = platform.getLastError();
|
granted = await platform.getPermissionsApi().request(getReadRequestPermissions());
|
||||||
if (lastError) {
|
} catch (lastError) {
|
||||||
console.error("error requesting permissions");
|
console.error("error requesting permissions");
|
||||||
console.error(lastError);
|
console.error(lastError);
|
||||||
onChange(false);
|
onChange(false);
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
// try {
|
console.log("permissions granted:", granted);
|
||||||
const res = await wxApi.setExtendedPermissions(granted);
|
const res = await wxApi.setExtendedPermissions(granted);
|
||||||
onChange(res.newValue);
|
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));
|
await wxApi.setExtendedPermissions(false).then(r => onChange(r.newValue));
|
||||||
return
|
return
|
||||||
|
@ -38,7 +38,7 @@ export interface CrossBrowserPermissionsApi {
|
|||||||
request(p: Permissions): Promise<boolean>;
|
request(p: Permissions): Promise<boolean>;
|
||||||
remove(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 {
|
export interface PlatformAPI {
|
||||||
/**
|
/**
|
||||||
|
* FIXME: should not be needed
|
||||||
|
*
|
||||||
* check if the platform is firefox
|
* check if the platform is firefox
|
||||||
*/
|
*/
|
||||||
isFirefox(): boolean;
|
isFirefox(): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Permission API for checking and add a listener
|
||||||
*/
|
*/
|
||||||
getPermissionsApi(): CrossBrowserPermissionsApi;
|
getPermissionsApi(): CrossBrowserPermissionsApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend API
|
||||||
|
*
|
||||||
|
* Register a callback to be called when the wallet is ready to start
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
notifyWhenAppIsReady(callback: () => void): void;
|
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;
|
openWalletPage(page: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup API
|
||||||
|
*
|
||||||
|
* Open a page into the wallet UI and closed the popup
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
openWalletPageFromPopup(page: string): void;
|
openWalletPageFromPopup(page: string): void;
|
||||||
setMessageToWalletBackground(operation: string, payload: any): Promise<CoreApiResponse>;
|
|
||||||
listenToWalletNotifications(listener: (m: any) => void): () => void;
|
/**
|
||||||
sendMessageToAllChannels(message: MessageFromBackend): void;
|
* Backend API
|
||||||
registerAllIncomingConnections(): void;
|
*
|
||||||
registerOnNewMessage(onNewMessage: (message: any, sender: any, callback: any) => void): void;
|
* When a tab has been detected to have a Taler action the background process
|
||||||
registerReloadOnNewVersion(): void;
|
* can use this function to redirect the tab to the wallet UI
|
||||||
|
*
|
||||||
|
* @param tabId
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
redirectTabToWalletPage(tabId: number, page: string): void;
|
redirectTabToWalletPage(tabId: number, page: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the wallet version from manifest
|
||||||
|
*/
|
||||||
getWalletVersion(): WalletVersion;
|
getWalletVersion(): WalletVersion;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend API
|
||||||
|
*/
|
||||||
|
registerAllIncomingConnections(): void;
|
||||||
|
/**
|
||||||
|
* Backend API
|
||||||
|
*/
|
||||||
|
registerReloadOnNewVersion(): void;
|
||||||
|
/**
|
||||||
|
* Backend API
|
||||||
|
*/
|
||||||
registerTalerHeaderListener(onHeader: (tabId: number, url: string) => void): void;
|
registerTalerHeaderListener(onHeader: (tabId: number, url: string) => void): void;
|
||||||
|
/**
|
||||||
|
* Backend API
|
||||||
|
*/
|
||||||
registerOnInstalled(callback: () => void): void;
|
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;
|
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>;
|
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;
|
export let platform: PlatformAPI = undefined as any;
|
||||||
|
@ -14,17 +14,16 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
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 { getReadRequestPermissions } from "../permissions";
|
||||||
import { CrossBrowserPermissionsApi, MessageFromBackend, Permissions, PlatformAPI } from "./api.js";
|
import { CrossBrowserPermissionsApi, MessageFromBackend, Permissions, PlatformAPI } from "./api.js";
|
||||||
|
|
||||||
const api: PlatformAPI = {
|
const api: PlatformAPI = {
|
||||||
isFirefox,
|
isFirefox,
|
||||||
findTalerUriInActiveTab,
|
findTalerUriInActiveTab,
|
||||||
getLastError,
|
|
||||||
getPermissionsApi,
|
getPermissionsApi,
|
||||||
getWalletVersion,
|
getWalletVersion,
|
||||||
listenToWalletNotifications,
|
listenToWalletBackground,
|
||||||
notifyWhenAppIsReady,
|
notifyWhenAppIsReady,
|
||||||
openWalletPage,
|
openWalletPage,
|
||||||
openWalletPageFromPopup,
|
openWalletPageFromPopup,
|
||||||
@ -32,12 +31,11 @@ const api: PlatformAPI = {
|
|||||||
redirectTabToWalletPage,
|
redirectTabToWalletPage,
|
||||||
registerAllIncomingConnections,
|
registerAllIncomingConnections,
|
||||||
registerOnInstalled,
|
registerOnInstalled,
|
||||||
registerOnNewMessage,
|
listenToAllChannels,
|
||||||
registerReloadOnNewVersion,
|
registerReloadOnNewVersion,
|
||||||
registerTalerHeaderListener,
|
registerTalerHeaderListener,
|
||||||
searchForTalerLinks,
|
|
||||||
sendMessageToAllChannels,
|
sendMessageToAllChannels,
|
||||||
setMessageToWalletBackground,
|
sendMessageToWalletBackground,
|
||||||
useServiceWorkerAsBackgroundProcess
|
useServiceWorkerAsBackgroundProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +48,7 @@ function isFirefox(): boolean {
|
|||||||
export function contains(p: Permissions): Promise<boolean> {
|
export function contains(p: Permissions): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
chrome.permissions.contains(p, (resp) => {
|
chrome.permissions.contains(p, (resp) => {
|
||||||
const le = getLastError()
|
const le = chrome.runtime.lastError?.message
|
||||||
if (le) {
|
if (le) {
|
||||||
rej(le)
|
rej(le)
|
||||||
}
|
}
|
||||||
@ -62,7 +60,7 @@ export function contains(p: Permissions): Promise<boolean> {
|
|||||||
export async function request(p: Permissions): Promise<boolean> {
|
export async function request(p: Permissions): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
chrome.permissions.request(p, (resp) => {
|
chrome.permissions.request(p, (resp) => {
|
||||||
const le = getLastError()
|
const le = chrome.runtime.lastError?.message
|
||||||
if (le) {
|
if (le) {
|
||||||
rej(le)
|
rej(le)
|
||||||
}
|
}
|
||||||
@ -74,7 +72,7 @@ export async function request(p: Permissions): Promise<boolean> {
|
|||||||
export async function remove(p: Permissions): Promise<boolean> {
|
export async function remove(p: Permissions): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
chrome.permissions.remove(p, (resp) => {
|
chrome.permissions.remove(p, (resp) => {
|
||||||
const le = getLastError()
|
const le = chrome.runtime.lastError?.message
|
||||||
if (le) {
|
if (le) {
|
||||||
rej(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");
|
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 {
|
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;
|
let url: string | undefined = undefined;
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case TalerUriType.TalerWithdraw:
|
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) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
|
chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
@ -164,7 +167,7 @@ async function setMessageToWalletBackground(operation: string, payload: any): Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
let notificationPort: chrome.runtime.Port | undefined;
|
let notificationPort: chrome.runtime.Port | undefined;
|
||||||
function listenToWalletNotifications(listener: (m: any) => void) {
|
function listenToWalletBackground(listener: (m: any) => void) {
|
||||||
if (notificationPort === undefined) {
|
if (notificationPort === undefined) {
|
||||||
notificationPort = chrome.runtime.connect({ name: "notifications" })
|
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) => {
|
chrome.runtime.onMessage.addListener((m, s, c) => {
|
||||||
cb(m, s, c)
|
cb(m, s, c)
|
||||||
|
|
||||||
@ -311,11 +314,6 @@ function useServiceWorkerAsBackgroundProcess() {
|
|||||||
return chrome.runtime.getManifest().manifest_version === 3
|
return chrome.runtime.getManifest().manifest_version === 3
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLastError() {
|
|
||||||
return chrome.runtime.lastError?.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function searchForTalerLinks(): string | undefined {
|
function searchForTalerLinks(): string | undefined {
|
||||||
let found;
|
let found;
|
||||||
found = document.querySelector("a[href^='taler://'")
|
found = document.querySelector("a[href^='taler://'")
|
||||||
|
159
packages/taler-wallet-webextension/src/platform/dev.ts
Normal file
159
packages/taler-wallet-webextension/src/platform/dev.ts
Normal file
@ -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;
|
||||||
|
|
183
packages/taler-wallet-webextension/src/popup/Application.tsx
Normal file
183
packages/taler-wallet-webextension/src/popup/Application.tsx
Normal file
@ -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;
|
||||||
|
}
|
@ -34,7 +34,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
|||||||
const uriType = classifyTalerUri(url);
|
const uriType = classifyTalerUri(url);
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
function redirectToWallet() {
|
function redirectToWallet() {
|
||||||
platform.openWalletURIFromPopup(uriType, url);
|
platform.openWalletURIFromPopup(url);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -21,29 +21,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { setupI18n } from "@gnu-taler/taler-util";
|
import { setupI18n } from "@gnu-taler/taler-util";
|
||||||
import { createHashHistory } from "history";
|
import { Fragment, h, render } from "preact";
|
||||||
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 { strings } from "./i18n/strings";
|
import { strings } from "./i18n/strings";
|
||||||
import { Pages, PopupNavBar } from "./NavigationBar";
|
import { setupPlatform } from "./platform/api";
|
||||||
import { platform, setupPlatform } from "./platform/api";
|
|
||||||
import chromeAPI from "./platform/chrome";
|
import chromeAPI from "./platform/chrome";
|
||||||
import firefoxAPI from "./platform/firefox";
|
import firefoxAPI from "./platform/firefox";
|
||||||
import { BalancePage } from "./popup/BalancePage";
|
import { Application } from "./popup/Application";
|
||||||
import { TalerActionFound } from "./popup/TalerActionFound";
|
|
||||||
import { BackupPage } from "./wallet/BackupPage";
|
//FIXME: create different entry point for any platform instead of
|
||||||
import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
|
//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 {
|
function main(): void {
|
||||||
try {
|
try {
|
||||||
@ -62,160 +56,8 @@ function main(): void {
|
|||||||
|
|
||||||
setupI18n("en", strings);
|
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") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", main);
|
document.addEventListener("DOMContentLoaded", main);
|
||||||
} else {
|
} else {
|
||||||
main();
|
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;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- 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"
|
<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">
|
||||||
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
|
||||||
<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" />
|
||||||
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>
|
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 556 B |
@ -15,7 +15,7 @@ export function AddNewActionView({ onCancel }: Props): VNode {
|
|||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
function redirectToWallet() {
|
function redirectToWallet() {
|
||||||
platform.openWalletURIFromPopup(uriType, url);
|
platform.openWalletURIFromPopup(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
263
packages/taler-wallet-webextension/src/wallet/Application.tsx
Normal file
263
packages/taler-wallet-webextension/src/wallet/Application.tsx
Normal file
@ -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;
|
||||||
|
}
|
@ -253,9 +253,6 @@ export function View({
|
|||||||
There is no known bank account to send money to
|
There is no known bank account to send money to
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
</p>
|
</p>
|
||||||
<ButtonBoxWarning>
|
|
||||||
<i18n.Translate>Withdraw</i18n.Translate>
|
|
||||||
</ButtonBoxWarning>
|
|
||||||
</WarningBox>
|
</WarningBox>
|
||||||
<footer>
|
<footer>
|
||||||
<Button onClick={onCancel}>
|
<Button onClick={onCancel}>
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -21,40 +21,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { setupI18n } from "@gnu-taler/taler-util";
|
import { setupI18n } from "@gnu-taler/taler-util";
|
||||||
import { createHashHistory } from "history";
|
import { Fragment, h, render } from "preact";
|
||||||
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 { strings } from "./i18n/strings";
|
import { strings } from "./i18n/strings";
|
||||||
import { Pages, WalletNavBar } from "./NavigationBar";
|
|
||||||
import { setupPlatform } from "./platform/api";
|
import { setupPlatform } from "./platform/api";
|
||||||
import chromeAPI from "./platform/chrome";
|
import chromeAPI from "./platform/chrome";
|
||||||
import firefoxAPI from "./platform/firefox";
|
import firefoxAPI from "./platform/firefox";
|
||||||
import { DeveloperPage } from "./popup/DeveloperPage";
|
import { Application } from "./wallet/Application";
|
||||||
import { BackupPage } from "./wallet/BackupPage";
|
|
||||||
import { DepositPage } from "./wallet/DepositPage";
|
const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
|
||||||
import { ExchangeAddPage } from "./wallet/ExchangeAddPage";
|
|
||||||
import { HistoryPage } from "./wallet/History";
|
//FIXME: create different entry point for any platform instead of
|
||||||
import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage";
|
//switching in runtime
|
||||||
import { ProviderAddPage } from "./wallet/ProviderAddPage";
|
if (isFirefox) {
|
||||||
import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
|
console.log("Wallet setup for Firefox API");
|
||||||
import { SettingsPage } from "./wallet/Settings";
|
setupPlatform(firefoxAPI);
|
||||||
import { TransactionPage } from "./wallet/Transaction";
|
} else {
|
||||||
import { WelcomePage } from "./wallet/Welcome";
|
console.log("Wallet setup for Chrome API");
|
||||||
|
setupPlatform(chromeAPI);
|
||||||
|
}
|
||||||
|
|
||||||
function main(): void {
|
function main(): void {
|
||||||
try {
|
try {
|
||||||
@ -73,230 +57,8 @@ function main(): void {
|
|||||||
|
|
||||||
setupI18n("en", strings);
|
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") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", main);
|
document.addEventListener("DOMContentLoaded", main);
|
||||||
} else {
|
} else {
|
||||||
main();
|
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;
|
|
||||||
}
|
|
||||||
|
@ -97,7 +97,7 @@ export interface UpgradeResponse {
|
|||||||
async function callBackend(operation: string, payload: any): Promise<any> {
|
async function callBackend(operation: string, payload: any): Promise<any> {
|
||||||
let response: CoreApiResponse;
|
let response: CoreApiResponse;
|
||||||
try {
|
try {
|
||||||
response = await platform.setMessageToWalletBackground(operation, payload);
|
response = await platform.sendMessageToWalletBackground(operation, payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Error calling backend");
|
console.log("Error calling backend");
|
||||||
throw new Error(`Error contacting backend: ${e}`);
|
throw new Error(`Error contacting backend: ${e}`);
|
||||||
@ -417,11 +417,11 @@ export function onUpdateNotification(
|
|||||||
messageTypes: Array<NotificationType>,
|
messageTypes: Array<NotificationType>,
|
||||||
doCallback: () => void,
|
doCallback: () => void,
|
||||||
): () => void {
|
): () => void {
|
||||||
const listener = (message: MessageFromBackend): void => {
|
const onNewMessage = (message: MessageFromBackend): void => {
|
||||||
const shouldNotify = messageTypes.includes(message.type);
|
const shouldNotify = messageTypes.includes(message.type);
|
||||||
if (shouldNotify) {
|
if (shouldNotify) {
|
||||||
doCallback();
|
doCallback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return platform.listenToWalletNotifications(listener);
|
return platform.listenToWalletBackground(onNewMessage);
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ export async function wxMain(): Promise<void> {
|
|||||||
|
|
||||||
// Handlers for messages coming directly from the content
|
// Handlers for messages coming directly from the content
|
||||||
// script on the page
|
// script on the page
|
||||||
platform.registerOnNewMessage((message, sender, callback) => {
|
platform.listenToAllChannels((message, sender, callback) => {
|
||||||
afterWalletIsInitialized.then(() => {
|
afterWalletIsInitialized.then(() => {
|
||||||
dispatch(message, sender, callback);
|
dispatch(message, sender, callback);
|
||||||
});
|
});
|
||||||
@ -285,8 +285,7 @@ export async function wxMain(): Promise<void> {
|
|||||||
|
|
||||||
// On platforms that support it, also listen to external
|
// On platforms that support it, also listen to external
|
||||||
// modification of permissions.
|
// modification of permissions.
|
||||||
platform.getPermissionsApi().addPermissionsListener((perm) => {
|
platform.getPermissionsApi().addPermissionsListener((perm, lastError) => {
|
||||||
const lastError = platform.getLastError()
|
|
||||||
if (lastError) {
|
if (lastError) {
|
||||||
console.error(lastError);
|
console.error(lastError);
|
||||||
return;
|
return;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<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" />
|
<link rel="icon" href="/static/img/icon.png" />
|
||||||
<script src="/dist/walletEntryPoint.js"></script>
|
<script src="/dist/walletEntryPoint.js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
Loading…
Reference in New Issue
Block a user