preact routing on the wallet

This commit is contained in:
Sebastian 2021-05-07 18:10:27 -03:00
parent 30f86f8748
commit 4ed4535bc0
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
23 changed files with 321 additions and 500 deletions

View File

@ -10,7 +10,8 @@
"scripts": { "scripts": {
"clean": "rimraf dist lib tsconfig.tsbuildinfo", "clean": "rimraf dist lib tsconfig.tsbuildinfo",
"test": "jest ./tests", "test": "jest ./tests",
"compile": "tsc && rollup -c" "compile": "tsc && rollup -c",
"watch": "tsc --watch & rollup -w -c"
}, },
"dependencies": { "dependencies": {
"@gnu-taler/taler-util": "workspace:*", "@gnu-taler/taler-util": "workspace:*",
@ -29,12 +30,14 @@
"@rollup/plugin-replace": "^2.3.4", "@rollup/plugin-replace": "^2.3.4",
"@testing-library/preact": "^2.0.1", "@testing-library/preact": "^2.0.1",
"@types/chrome": "^0.0.128", "@types/chrome": "^0.0.128",
"@types/history": "^4.7.8",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",
"@types/node": "^14.14.22", "@types/node": "^14.14.22",
"ava": "3.15.0", "ava": "3.15.0",
"babel-plugin-transform-react-jsx": "^6.24.1", "babel-plugin-transform-react-jsx": "^6.24.1",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-adapter-preact-pure": "^3.1.0", "enzyme-adapter-preact-pure": "^3.1.0",
"history": "4.10.1",
"jest": "^26.6.3", "jest": "^26.6.3",
"jest-preset-preact": "^4.0.3", "jest-preset-preact": "^4.0.3",
"preact-cli": "^3.0.5", "preact-cli": "^3.0.5",

View File

@ -0,0 +1,99 @@
import Router, { route, Route } from "preact-router";
import { createHashHistory } from 'history';
import { useEffect } from "preact/hooks";
import { WalletPopup } from "./pages/popup";
import { WithdrawalDialog } from "./pages/withdraw";
import { Welcome } from "./pages/welcome";
import { TalerPayDialog } from "./pages/pay";
import { RefundStatusView } from "./pages/refund";
import { TalerTipDialog } from './pages/tip';
export enum Pages {
welcome = '/welcome',
pay = '/pay',
payback = '/payback',
refund = '/refund',
reset_required = '/reset-required',
return_coins = '/return-coins',
tips = '/tips',
withdraw = '/withdraw',
popup = '/popup/:rest',
}
export function Application() {
const sp = new URL(document.location.href).searchParams
const queryParams: any = {}
sp.forEach((v, k) => { queryParams[k] = v; });
return <Router history={createHashHistory()} >
<Route path={Pages.popup} component={WalletPopup} />
<Route path={Pages.welcome} component={() => {
return <section id="main">
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
<h1 style="font-family: monospace; font-size: 250%;">
<span style="color: #aa3939;"></span>Taler Wallet<span style="color: #aa3939;"></span>
</h1>
</div>
<h1>Browser Extension Installed!</h1>
<div>
<Welcome />
</div>
</section>
}} />
<Route path={Pages.pay} component={() => {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<article class="fade">
<TalerPayDialog talerPayUri={queryParams.talerPayUri} />
</article>
</section>
}} />
<Route path={Pages.refund} component={() => {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<article class="fade">
<RefundStatusView talerRefundUri={queryParams.talerRefundUri} />
</article>
</section>
}} />
<Route path={Pages.tips} component={() => {
return <section id="main">
<h1>GNU Taler Wallet</h1>
<div>
<TalerTipDialog talerTipUri={queryParams.talerTipUri} />
</div>
</section>
}} />
<Route path={Pages.withdraw} component={() => {
return <section id="main">
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
<h1 style="font-family: monospace; font-size: 250%;">
<span style="color: #aa3939;"></span>Taler Wallet<span style="color: #aa3939;"></span>
</h1>
</div>
<div class="fade">
<WithdrawalDialog talerWithdrawUri={queryParams.talerWithdrawUri} />
</div>
</section>
}} />
<Route path={Pages.reset_required} component={() => <div>no yet implemented</div>} />
<Route path={Pages.payback} component={() => <div>no yet implemented</div>} />
<Route path={Pages.return_coins} component={() => <div>no yet implemented</div>} />
<Route default component={Redirect} to='/popup/balance' />
</Router>
}
export function Redirect({ to }: { to: string }): null {
useEffect(() => {
route(to, true)
})
return null
}

View File

@ -20,50 +20,17 @@
* @author Florian Dold <dold@taler.net> * @author Florian Dold <dold@taler.net>
*/ */
import {render} from "preact"; import { render } from "preact";
import { createPopup } from "./pages/popup";
import { createWithdrawPage } from "./pages/withdraw";
import { createWelcomePage } from "./pages/welcome";
import { createPayPage } from "./pages/pay";
import { createRefundPage } from "./pages/refund";
import { setupI18n } from "@gnu-taler/taler-wallet-core"; import { setupI18n } from "@gnu-taler/taler-wallet-core";
import { createTipPage } from './pages/tip'; import { Application } from './Application';
function main(): void { function main(): void {
try { try {
let mainElement;
const m = location.pathname.match(/([^/]+)$/);
if (!m) {
throw Error("can't parse page URL");
}
const page = m[1];
switch (page) {
case "popup.html":
mainElement = createPopup();
break;
case "withdraw.html":
mainElement = createWithdrawPage();
break;
case "welcome.html":
mainElement = createWelcomePage();
break;
case "pay.html":
mainElement = createPayPage();
break;
case "refund.html":
mainElement = createRefundPage();
break;
case "tip.html":
mainElement = createTipPage();
break;
default:
throw Error(`page '${page}' not implemented`);
}
const container = document.getElementById("container"); const container = document.getElementById("container");
if (!container) { if (!container) {
throw Error("container not found, can't mount page contents"); throw Error("container not found, can't mount page contents");
} }
render(mainElement, container); render(Application(), container);
} catch (e) { } catch (e) {
console.error("got error", e); console.error("got error", e);
document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;

View File

@ -41,7 +41,11 @@ import {
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { JSX, VNode } from "preact"; import { JSX, VNode } from "preact";
function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element { interface Props {
talerPayUri?: string
}
export function TalerPayDialog({ talerPayUri }: Props): JSX.Element {
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined); const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined);
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined); const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
const [payErrMsg, setPayErrMsg] = useState<string | undefined>(""); const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
@ -50,6 +54,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
let totalFees: AmountJson | undefined = undefined; let totalFees: AmountJson | undefined = undefined;
useEffect(() => { useEffect(() => {
if (!talerPayUri) return;
const doFetch = async (): Promise<void> => { const doFetch = async (): Promise<void> => {
const p = await wxApi.preparePay(talerPayUri); const p = await wxApi.preparePay(talerPayUri);
setPayStatus(p); setPayStatus(p);
@ -57,6 +62,10 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
doFetch(); doFetch();
}, [numTries, talerPayUri]); }, [numTries, talerPayUri]);
if (!talerPayUri) {
return <span>missing pay uri</span>
}
if (!payStatus) { if (!payStatus) {
return <span>Loading payment information ...</span>; return <span>Loading payment information ...</span>;
} }
@ -83,7 +92,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
return ( return (
<span> <span>
You have already paid for this article. Click{" "} You have already paid for this article. Click{" "}
<a href={fulfillmentUrl}>here</a> to view it again. <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
</span> </span>
); );
} else { } else {

View File

@ -24,9 +24,7 @@
/** /**
* Imports. * Imports.
*/ */
import * as i18n from "../i18n"; import {
import {
AmountJson, AmountJson,
Amounts, Amounts,
BalancesResponse, BalancesResponse,
@ -40,121 +38,43 @@ import {
Timestamp, Timestamp,
amountFractionalBase, amountFractionalBase,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Component, ComponentChildren, JSX } from "preact";
import { renderAmount, PageLink } from "../renderHtml"; import { route, Route, Router } from 'preact-router';
import { Match } from 'preact-router/match';
import { useEffect, useState } from "preact/hooks";
import * as i18n from "../i18n";
import { PageLink, renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
import { useState, useEffect } from "preact/hooks";
import { PermissionsCheckbox } from "./welcome"; import { PermissionsCheckbox } from "./welcome";
import { JSXInternal } from "preact/src/jsx";
import { Component, ComponentChild, ComponentChildren, JSX, toChildArray, VNode } from "preact";
// FIXME: move to newer react functions
class Router extends Component<any, any> {
static setRoute(s: string): void {
window.location.hash = s;
}
static getRoute(): string {
// Omit the '#' at the beginning
return window.location.hash.substring(1);
}
static onRoute(f: any): () => void {
Router.routeHandlers.push(f);
return () => {
const i = Router.routeHandlers.indexOf(f);
this.routeHandlers = this.routeHandlers.splice(i, 1);
};
}
private static routeHandlers: any[] = [];
componentWillMount(): void {
console.log("router mounted");
window.onhashchange = () => {
this.setState({});
for (const f of Router.routeHandlers) {
f();
}
};
}
render(): JSX.Element {
const route = window.location.hash.substring(1);
console.log("rendering route", route);
let defaultChild: ComponentChild | null = null;
let foundChild: ComponentChild | null = null;
toChildArray(this.props.children).forEach((child) => {
const childProps: any = (child as any).props;
if (!childProps) {
return;
}
if (childProps.default) {
defaultChild = child;
}
if (childProps.route === route) {
foundChild = child;
}
});
const c: ComponentChild | null = foundChild || defaultChild;
if (!c) {
throw Error("unknown route");
}
Router.setRoute((c as any).props.route);
return <div>{c}</div>;
}
}
interface TabProps { interface TabProps {
target: string; target: string;
current?: string;
children?: ComponentChildren; children?: ComponentChildren;
} }
function Tab(props: TabProps): JSX.Element { function Tab(props: TabProps): JSX.Element {
let cssClass = ""; let cssClass = "";
if (props.target === Router.getRoute()) { if (props.current === props.target) {
cssClass = "active"; cssClass = "active";
} }
const onClick = (e: JSXInternal.TargetedMouseEvent<HTMLAnchorElement>): void => {
Router.setRoute(props.target);
e.preventDefault();
};
return ( return (
<a onClick={onClick} href={props.target} className={cssClass}> <a href={props.target} className={cssClass}>
{props.children} {props.children}
</a> </a>
); );
} }
class WalletNavBar extends Component<any, any> { function WalletNavBar({ current }: { current?: string }) {
private cancelSubscription: any;
componentWillMount(): void {
this.cancelSubscription = Router.onRoute(() => {
this.setState({});
});
}
componentWillUnmount(): void {
if (this.cancelSubscription) {
this.cancelSubscription();
}
}
render(): JSX.Element {
console.log("rendering nav bar");
return ( return (
<div className="nav" id="header"> <div className="nav" id="header">
<Tab target="/balance">{i18n.str`Balance`}</Tab> <Tab target="/popup/balance" current={current}>{i18n.str`Balance`}</Tab>
<Tab target="/history">{i18n.str`History`}</Tab> <Tab target="/popup/history" current={current}>{i18n.str`History`}</Tab>
<Tab target="/settings">{i18n.str`Settings`}</Tab> <Tab target="/popup/settings" current={current}>{i18n.str`Settings`}</Tab>
<Tab target="/debug">{i18n.str`Debug`}</Tab> <Tab target="/popup/debug" current={current}>{i18n.str`Debug`}</Tab>
</div> </div>
); );
}
} }
/** /**
@ -174,7 +94,7 @@ function EmptyBalanceView(): JSX.Element {
return ( return (
<i18n.Translate wrap="p"> <i18n.Translate wrap="p">
You have no balance to show. Need some{" "} You have no balance to show. Need some{" "}
<PageLink pageName="welcome.html">help</PageLink> getting started? <PageLink pageName="/welcome">help</PageLink> getting started?
</i18n.Translate> </i18n.Translate>
); );
} }
@ -494,7 +414,7 @@ function WalletHistory(props: any): JSX.Element {
return ( return (
<div> <div>
{txs.map((tx,i) => ( {txs.map((tx, i) => (
<TransactionItem key={i} tx={tx} /> <TransactionItem key={i} tx={tx} />
))} ))}
</div> </div>
@ -634,7 +554,7 @@ async function findTalerUriInActiveTab(): Promise<string | undefined> {
}); });
} }
function WalletPopup(): JSX.Element { export function WalletPopup(): JSX.Element {
const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>( const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
undefined, undefined,
); );
@ -671,19 +591,29 @@ function WalletPopup(): JSX.Element {
} }
return ( return (
<div> <div>
<WalletNavBar /> <Match>{({ path }: any) => <WalletNavBar current={path} />}</Match>
<div style={{ margin: "1em" }}> <div style={{ margin: "1em" }}>
<Router> <Router>
<WalletBalanceView route="/balance" default /> <Route path={Pages.balance} component={WalletBalanceView} />
<WalletSettings route="/settings" /> <Route path={Pages.settings} component={WalletSettings} />
<WalletDebug route="/debug" /> <Route path={Pages.debug} component={WalletDebug} />
<WalletHistory route="/history" /> <Route path={Pages.history} component={WalletHistory} />
</Router> </Router>
</div> </div>
</div> </div>
); );
} }
export function createPopup(): JSX.Element { enum Pages {
return <WalletPopup />; balance = '/popup/balance',
settings = '/popup/settings',
debug = '/popup/debug',
history = '/popup/history',
}
export function Redirect({ to }: { to: string }): null {
useEffect(() => {
route(to, true)
})
return null
} }

View File

@ -26,18 +26,22 @@ import {
ApplyRefundResponse, ApplyRefundResponse,
Amounts, Amounts,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
// import { h } from 'preact';
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { JSX } from "preact/jsx-runtime"; import { JSX } from "preact/jsx-runtime";
function RefundStatusView(props: { talerRefundUri: string }): JSX.Element { interface Props {
const [applyResult, setApplyResult] = useState<ApplyRefundResponse|undefined>(undefined); talerRefundUri?: string
}
export function RefundStatusView({ talerRefundUri }: Props): JSX.Element {
const [applyResult, setApplyResult] = useState<ApplyRefundResponse | undefined>(undefined);
const [errMsg, setErrMsg] = useState<string | undefined>(undefined); const [errMsg, setErrMsg] = useState<string | undefined>(undefined);
useEffect(() => { useEffect(() => {
if (!talerRefundUri) return;
const doFetch = async (): Promise<void> => { const doFetch = async (): Promise<void> => {
try { try {
const result = await wxApi.applyRefund(props.talerRefundUri); const result = await wxApi.applyRefund(talerRefundUri);
setApplyResult(result); setApplyResult(result);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -46,10 +50,14 @@ function RefundStatusView(props: { talerRefundUri: string }): JSX.Element {
} }
}; };
doFetch(); doFetch();
}, [props.talerRefundUri]); }, [talerRefundUri]);
console.log("rendering"); console.log("rendering");
if (!talerRefundUri) {
return <span>missing taler refund uri</span>;
}
if (errMsg) { if (errMsg) {
return <span>Error: {errMsg}</span>; return <span>Error: {errMsg}</span>;
} }

View File

@ -26,7 +26,11 @@ import { AmountView } from "../renderHtml";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
import { JSX } from "preact/jsx-runtime"; import { JSX } from "preact/jsx-runtime";
function TalerTipDialog({ talerTipUri }: { talerTipUri: string }): JSX.Element { interface Props {
talerTipUri?: string
}
export function TalerTipDialog({ talerTipUri }: Props): JSX.Element {
const [updateCounter, setUpdateCounter] = useState<number>(0); const [updateCounter, setUpdateCounter] = useState<number>(0);
const [prepareTipResult, setPrepareTipResult] = useState< const [prepareTipResult, setPrepareTipResult] = useState<
PrepareTipResult | undefined PrepareTipResult | undefined
@ -35,6 +39,7 @@ function TalerTipDialog({ talerTipUri }: { talerTipUri: string }): JSX.Element {
const [tipIgnored, setTipIgnored] = useState(false); const [tipIgnored, setTipIgnored] = useState(false);
useEffect(() => { useEffect(() => {
if (!talerTipUri) return;
const doFetch = async (): Promise<void> => { const doFetch = async (): Promise<void> => {
const p = await wxApi.prepareTip({ talerTipUri }); const p = await wxApi.prepareTip({ talerTipUri });
setPrepareTipResult(p); setPrepareTipResult(p);
@ -54,6 +59,10 @@ function TalerTipDialog({ talerTipUri }: { talerTipUri: string }): JSX.Element {
setTipIgnored(true); setTipIgnored(true);
}; };
if (!talerTipUri) {
return <span>missing tip uri</span>;
}
if (tipIgnored) { if (tipIgnored) {
return <span>You've ignored the tip.</span>; return <span>You've ignored the tip.</span>;
} }

View File

@ -87,7 +87,7 @@ function Diagnostics(): JSX.Element | null {
<p> <p>
Your wallet database is outdated. Currently automatic migration is Your wallet database is outdated. Currently automatic migration is
not supported. Please go{" "} not supported. Please go{" "}
<PageLink pageName="reset-required.html">here</PageLink> to reset <PageLink pageName="/reset-required">here</PageLink> to reset
the wallet database. the wallet database.
</p> </p>
) : null} ) : null}
@ -99,13 +99,11 @@ function Diagnostics(): JSX.Element | null {
return <p>Running diagnostics ...</p>; return <p>Running diagnostics ...</p>;
} }
export function PermissionsCheckbox(): JSX.Element {
const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState( async function handleExtendedPerm(isEnabled: boolean, setEnable: (v:boolean) => void): Promise<void> {
false,
);
async function handleExtendedPerm(): Promise<void> {
let nextVal: boolean | undefined; let nextVal: boolean | undefined;
if (extendedPermissionsEnabled) {
if (!isEnabled) {
const granted = await new Promise<boolean>((resolve, reject) => { const granted = await new Promise<boolean>((resolve, reject) => {
// 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 ...
@ -121,16 +119,18 @@ export function PermissionsCheckbox(): JSX.Element {
}); });
}); });
const res = await wxApi.setExtendedPermissions(granted); const res = await wxApi.setExtendedPermissions(granted);
console.log(res);
nextVal = res.newValue; nextVal = res.newValue;
} else { } else {
const res = await wxApi.setExtendedPermissions(false); const res = await wxApi.setExtendedPermissions(false);
console.log(res);
nextVal = res.newValue; nextVal = res.newValue;
} }
console.log("new permissions applied:", nextVal); console.log("new permissions applied:", nextVal);
setExtendedPermissionsEnabled(nextVal ?? false); setEnable(nextVal ?? false);
} }
export function PermissionsCheckbox(): JSX.Element {
const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState(false);
useEffect(() => { useEffect(() => {
async function getExtendedPermValue(): Promise<void> { async function getExtendedPermValue(): Promise<void> {
const res = await wxApi.getExtendedPermissions(); const res = await wxApi.getExtendedPermissions();
@ -138,11 +138,12 @@ export function PermissionsCheckbox(): JSX.Element {
} }
getExtendedPermValue(); getExtendedPermValue();
}); });
return ( return (
<div> <div>
<input <input
checked={extendedPermissionsEnabled} checked={extendedPermissionsEnabled}
onChange={() => handleExtendedPerm()} onChange={() => handleExtendedPerm(extendedPermissionsEnabled, setExtendedPermissionsEnabled) }
type="checkbox" type="checkbox"
id="checkbox-perm" id="checkbox-perm"
style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }}
@ -168,7 +169,7 @@ export function PermissionsCheckbox(): JSX.Element {
); );
} }
function Welcome(): JSX.Element { export function Welcome(): JSX.Element {
return ( return (
<> <>
<p>Thank you for installing the wallet.</p> <p>Thank you for installing the wallet.</p>

View File

@ -34,12 +34,14 @@ import {
import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util"; import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util";
import { JSX } from "preact/jsx-runtime"; import { JSX } from "preact/jsx-runtime";
function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element { interface Props {
talerWithdrawUri?: string;
}
export function WithdrawalDialog({ talerWithdrawUri }: Props): JSX.Element {
const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined); const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined);
const [selectedExchange, setSelectedExchange] = useState< const [selectedExchange, setSelectedExchange] = useState<
string | undefined string | undefined
>(undefined); >(undefined);
const talerWithdrawUri = props.talerWithdrawUri;
const [cancelled, setCancelled] = useState(false); const [cancelled, setCancelled] = useState(false);
const [selecting, setSelecting] = useState(false); const [selecting, setSelecting] = useState(false);
const [errMsg, setErrMsg] = useState<string | undefined>(""); const [errMsg, setErrMsg] = useState<string | undefined>("");
@ -52,10 +54,9 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!talerWithdrawUri) return
const fetchData = async (): Promise<void> => { const fetchData = async (): Promise<void> => {
const res = await getWithdrawalDetailsForUri({ const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
talerWithdrawUri: props.talerWithdrawUri,
});
setDetails(res); setDetails(res);
if (res.defaultExchangeBaseUrl) { if (res.defaultExchangeBaseUrl) {
setSelectedExchange(res.defaultExchangeBaseUrl); setSelectedExchange(res.defaultExchangeBaseUrl);
@ -64,6 +65,10 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
fetchData(); fetchData();
}, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]); }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]);
if (!talerWithdrawUri) {
return <span>missing withdraw uri</span>;
}
if (!details) { if (!details) {
return <span>Loading...</span>; return <span>Loading...</span>;
} }

View File

@ -167,7 +167,7 @@ export function ProgressButton({isLoading, ...rest}: LoadingButtonProps): JSX.El
export function PageLink( export function PageLink(
props: { pageName: string, children?: ComponentChildren }, props: { pageName: string, children?: ComponentChildren },
): JSX.Element { ): JSX.Element {
const url = chrome.extension.getURL(`/${props.pageName}`); const url = chrome.extension.getURL(`/static/popup.html#/${props.pageName}`);
return ( return (
<a <a
className="actionLink" className="actionLink"

View File

@ -276,7 +276,7 @@ try {
chrome.runtime.onInstalled.addListener((details) => { chrome.runtime.onInstalled.addListener((details) => {
console.log("onInstalled with reason", details.reason); console.log("onInstalled with reason", details.reason);
if (details.reason === "install") { if (details.reason === "install") {
const url = chrome.extension.getURL("/static/welcome.html"); const url = chrome.extension.getURL("/static/popup.html#/welcome");
chrome.tabs.create({ active: true, url: url }); chrome.tabs.create({ active: true, url: url });
} }
}); });
@ -311,7 +311,7 @@ function headerListener(
switch (uriType) { switch (uriType) {
case TalerUriType.TalerWithdraw: case TalerUriType.TalerWithdraw:
return makeSyncWalletRedirect( return makeSyncWalletRedirect(
"/static/withdraw.html", "/static/popup.html#/withdraw",
details.tabId, details.tabId,
details.url, details.url,
{ {
@ -320,7 +320,7 @@ function headerListener(
); );
case TalerUriType.TalerPay: case TalerUriType.TalerPay:
return makeSyncWalletRedirect( return makeSyncWalletRedirect(
"/static/pay.html", "/static/popup.html#/pay",
details.tabId, details.tabId,
details.url, details.url,
{ {
@ -329,7 +329,7 @@ function headerListener(
); );
case TalerUriType.TalerTip: case TalerUriType.TalerTip:
return makeSyncWalletRedirect( return makeSyncWalletRedirect(
"/static/tip.html", "/static/popup.html#/tip",
details.tabId, details.tabId,
details.url, details.url,
{ {
@ -338,7 +338,7 @@ function headerListener(
); );
case TalerUriType.TalerRefund: case TalerUriType.TalerRefund:
return makeSyncWalletRedirect( return makeSyncWalletRedirect(
"/static/refund.html", "/static/popup.html#/refund",
details.tabId, details.tabId,
details.url, details.url,
{ {

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Add Auditor</title>
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
<link rel="icon" href="/img/icon.png" />
<script src="/pageEntryPoint.js"></script>
<style>
.tree-item {
margin: 2em;
border-radius: 5px;
border: 1px solid gray;
padding: 1em;
}
.button-linky {
background: none;
color: black;
text-decoration: underline;
border: none;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Auditors</title>
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
<link rel="icon" href="/img/icon.png" />
<script src="/dist/webextension/pageEntryPoint.js"></script>
<style>
body {
font-size: 100%;
}
.tree-item {
margin: 2em;
border-radius: 5px;
border: 1px solid gray;
padding: 1em;
}
.button-linky {
background: none;
color: black;
text-decoration: underline;
border: none;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>

View File

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Benchmarks</title>
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<link rel="icon" href="/static/img/icon.png" />
<script src="/dist/pageEntryPoint.js"></script>
</head>
<body>
<section id="main">
<h1>Benchmarks</h1>
<div id="container"></div>
</section>
</body>
</html>

View File

@ -1,73 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Confirm Contract</title>
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<link rel="icon" href="/static/img/icon.png" />
<script src="/dist/pageEntryPoint.js"></script>
<style>
button.accept {
background-color: #5757d2;
border: 1px solid black;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background: none !important;
border: none;
padding: 0 !important;
font-family: arial, sans-serif;
color: #069;
text-decoration: underline;
cursor: pointer;
}
input.url {
width: 25em;
}
button.accept:disabled {
background-color: #dedbe8;
border: 1px solid white;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: #2c2c2c;
}
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #ff8a8a;
}
.okaybox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #00fa9a;
}
</style>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<article id="container" class="fade"></article>
</section>
</body>
</html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Payback</title>
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
<link rel="icon" href="/img/icon.png" />
<script src="/pageEntryPoint.js"></script>
<style>
body {
font-size: 100%;
}
.tree-item {
margin: 2em;
border-radius: 5px;
border: 1px solid gray;
padding: 1em;
}
.button-linky {
background: none;
color: black;
text-decoration: underline;
border: none;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Refund Status</title>
<link rel="icon" href="/static/img/icon.png" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<script src="/dist/pageEntryPoint.js"></script>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<article id="container" class="fade"></article>
</section>
</body>
</html>

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Select Taler Provider</title>
<link rel="icon" href="/img/icon.png" />
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
<script src="/pageEntryPoint.js"></script>
<style>
body {
font-size: 100%;
overflow-y: scroll;
}
</style>
</head>
<body>
<section id="main">
<div id="container"></div>
</section>
</body>
</html>

View File

@ -6,7 +6,7 @@
body { body {
min-height: 20em; min-height: 20em;
width: 30em; /* width: 30em; */
margin: 0; margin: 0;
padding: 0; padding: 0;
max-height: 800px; max-height: 800px;
@ -183,3 +183,58 @@ input[type="radio"] {
text-align: center; text-align: center;
padding-top: 2em; padding-top: 2em;
} }
/**
pay html
*/
button.accept {
background-color: #5757d2;
border: 1px solid black;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background: none !important;
border: none;
padding: 0 !important;
font-family: arial, sans-serif;
color: #069;
text-decoration: underline;
cursor: pointer;
}
input.url {
width: 25em;
}
button.accept:disabled {
background-color: #dedbe8;
border: 1px solid white;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: #2c2c2c;
}
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #ff8a8a;
}
.okaybox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #00fa9a;
}

View File

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Received Tip</title>
<link rel="icon" href="/static/img/icon.png" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<script src="/dist/pageEntryPoint.js"></script>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
<div id="container"></div>
</section>
</body>
</html>

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet Installed</title>
<link rel="icon" href="/static/img/icon.png" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<script src="/dist/pageEntryPoint.js"></script>
</head>
<body>
<section id="main">
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
<h1 style="font-family: monospace; font-size: 250%;">
<span style="color: #aa3939;"></span>Taler Wallet<span style="color: #aa3939;"></span>
</h1>
</div>
<h1>Browser Extension Installed!</h1>
<div id="container">Loading...</div>
</section>
</body>
</html>

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Taler Wallet: Withdraw</title>
<link rel="icon" href="/static/img/icon.png" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
<script src="/dist/pageEntryPoint.js"></script>
</head>
<body>
<section id="main">
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
<h1 style="font-family: monospace; font-size: 250%;">
<span style="color: #aa3939;"></span>Taler Wallet<span style="color: #aa3939;"></span>
</h1>
</div>
<div class="fade" id="container"></div>
</section>
</body>
</html>

View File

@ -216,12 +216,14 @@ importers:
'@rollup/plugin-replace': ^2.3.4 '@rollup/plugin-replace': ^2.3.4
'@testing-library/preact': ^2.0.1 '@testing-library/preact': ^2.0.1
'@types/chrome': ^0.0.128 '@types/chrome': ^0.0.128
'@types/history': ^4.7.8
'@types/jest': ^26.0.23 '@types/jest': ^26.0.23
'@types/node': ^14.14.22 '@types/node': ^14.14.22
ava: 3.15.0 ava: 3.15.0
babel-plugin-transform-react-jsx: ^6.24.1 babel-plugin-transform-react-jsx: ^6.24.1
enzyme: ^3.11.0 enzyme: ^3.11.0
enzyme-adapter-preact-pure: ^3.1.0 enzyme-adapter-preact-pure: ^3.1.0
history: 4.10.1
jest: ^26.6.3 jest: ^26.6.3
jest-preset-preact: ^4.0.3 jest-preset-preact: ^4.0.3
preact: ^10.5.13 preact: ^10.5.13
@ -251,12 +253,14 @@ importers:
'@rollup/plugin-replace': 2.3.4_rollup@2.37.1 '@rollup/plugin-replace': 2.3.4_rollup@2.37.1
'@testing-library/preact': 2.0.1_preact@10.5.13 '@testing-library/preact': 2.0.1_preact@10.5.13
'@types/chrome': 0.0.128 '@types/chrome': 0.0.128
'@types/history': 4.7.8
'@types/jest': 26.0.23 '@types/jest': 26.0.23
'@types/node': 14.14.22 '@types/node': 14.14.22
ava: 3.15.0 ava: 3.15.0
babel-plugin-transform-react-jsx: 6.24.1 babel-plugin-transform-react-jsx: 6.24.1
enzyme: 3.11.0 enzyme: 3.11.0
enzyme-adapter-preact-pure: 3.1.0_enzyme@3.11.0+preact@10.5.13 enzyme-adapter-preact-pure: 3.1.0_enzyme@3.11.0+preact@10.5.13
history: 4.10.1
jest: 26.6.3 jest: 26.6.3
jest-preset-preact: 4.0.3_669f037bdb6c36f0a67e918c516dafdd jest-preset-preact: 4.0.3_669f037bdb6c36f0a67e918c516dafdd
preact-cli: 3.0.5_c069246dc1d99535ac277c76f8ef56e0 preact-cli: 3.0.5_c069246dc1d99535ac277c76f8ef56e0
@ -2178,6 +2182,10 @@ packages:
resolution: {integrity: sha512-IG8AE1m2pWtPqQ7wXhFhy6Q59bwwnLwO36v5Rit2FrbXCIp8Sk8E2PfUCreyrdo17STwFSKDAkitVuVYbpEHvQ==} resolution: {integrity: sha512-IG8AE1m2pWtPqQ7wXhFhy6Q59bwwnLwO36v5Rit2FrbXCIp8Sk8E2PfUCreyrdo17STwFSKDAkitVuVYbpEHvQ==}
dev: true dev: true
/@types/history/4.7.8:
resolution: {integrity: sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==}
dev: true
/@types/istanbul-lib-coverage/2.0.3: /@types/istanbul-lib-coverage/2.0.3:
resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==} resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==}
dev: true dev: true
@ -6569,6 +6577,17 @@ packages:
resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==} resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==}
dev: true dev: true
/history/4.10.1:
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
dependencies:
'@babel/runtime': 7.14.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.1.0
tiny-warning: 1.0.3
value-equal: 1.0.1
dev: true
/hmac-drbg/1.0.1: /hmac-drbg/1.0.1:
resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=} resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=}
dependencies: dependencies:
@ -10818,6 +10837,10 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/resolve-pathname/3.0.0:
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
dev: true
/resolve-url/0.2.1: /resolve-url/0.2.1:
resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=}
deprecated: https://github.com/lydell/resolve-url#deprecated deprecated: https://github.com/lydell/resolve-url#deprecated
@ -12097,6 +12120,14 @@ packages:
resolution: {integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=} resolution: {integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=}
dev: true dev: true
/tiny-invariant/1.1.0:
resolution: {integrity: sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==}
dev: true
/tiny-warning/1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: true
/tmpl/1.0.4: /tmpl/1.0.4:
resolution: {integrity: sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=} resolution: {integrity: sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=}
dev: true dev: true
@ -12674,6 +12705,10 @@ packages:
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
dev: true dev: true
/value-equal/1.0.1:
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
dev: true
/vary/1.1.2: /vary/1.1.2:
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}