preact routing on the wallet
This commit is contained in:
parent
30f86f8748
commit
4ed4535bc0
@ -10,7 +10,8 @@
|
||||
"scripts": {
|
||||
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
||||
"test": "jest ./tests",
|
||||
"compile": "tsc && rollup -c"
|
||||
"compile": "tsc && rollup -c",
|
||||
"watch": "tsc --watch & rollup -w -c"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnu-taler/taler-util": "workspace:*",
|
||||
@ -29,12 +30,14 @@
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
"@testing-library/preact": "^2.0.1",
|
||||
"@types/chrome": "^0.0.128",
|
||||
"@types/history": "^4.7.8",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/node": "^14.14.22",
|
||||
"ava": "3.15.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-preact-pure": "^3.1.0",
|
||||
"history": "4.10.1",
|
||||
"jest": "^26.6.3",
|
||||
"jest-preset-preact": "^4.0.3",
|
||||
"preact-cli": "^3.0.5",
|
||||
|
99
packages/taler-wallet-webextension/src/Application.tsx
Normal file
99
packages/taler-wallet-webextension/src/Application.tsx
Normal 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
|
||||
}
|
@ -20,50 +20,17 @@
|
||||
* @author Florian Dold <dold@taler.net>
|
||||
*/
|
||||
|
||||
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 { render } from "preact";
|
||||
import { setupI18n } from "@gnu-taler/taler-wallet-core";
|
||||
import { createTipPage } from './pages/tip';
|
||||
import { Application } from './Application';
|
||||
|
||||
function main(): void {
|
||||
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");
|
||||
if (!container) {
|
||||
throw Error("container not found, can't mount page contents");
|
||||
}
|
||||
render(mainElement, container);
|
||||
render(Application(), container);
|
||||
} catch (e) {
|
||||
console.error("got error", e);
|
||||
document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
|
||||
|
@ -41,7 +41,11 @@ import {
|
||||
} from "@gnu-taler/taler-util";
|
||||
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 [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
|
||||
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
|
||||
@ -50,6 +54,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
||||
let totalFees: AmountJson | undefined = undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (!talerPayUri) return;
|
||||
const doFetch = async (): Promise<void> => {
|
||||
const p = await wxApi.preparePay(talerPayUri);
|
||||
setPayStatus(p);
|
||||
@ -57,6 +62,10 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
||||
doFetch();
|
||||
}, [numTries, talerPayUri]);
|
||||
|
||||
if (!talerPayUri) {
|
||||
return <span>missing pay uri</span>
|
||||
}
|
||||
|
||||
if (!payStatus) {
|
||||
return <span>Loading payment information ...</span>;
|
||||
}
|
||||
@ -83,7 +92,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
||||
return (
|
||||
<span>
|
||||
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>
|
||||
);
|
||||
} else {
|
||||
|
@ -24,9 +24,7 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import * as i18n from "../i18n";
|
||||
|
||||
import {
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
BalancesResponse,
|
||||
@ -40,121 +38,43 @@ import {
|
||||
Timestamp,
|
||||
amountFractionalBase,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
import { renderAmount, PageLink } from "../renderHtml";
|
||||
import { Component, ComponentChildren, JSX } from "preact";
|
||||
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 { useState, useEffect } from "preact/hooks";
|
||||
|
||||
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 {
|
||||
target: string;
|
||||
current?: string;
|
||||
children?: ComponentChildren;
|
||||
}
|
||||
|
||||
function Tab(props: TabProps): JSX.Element {
|
||||
let cssClass = "";
|
||||
if (props.target === Router.getRoute()) {
|
||||
if (props.current === props.target) {
|
||||
cssClass = "active";
|
||||
}
|
||||
const onClick = (e: JSXInternal.TargetedMouseEvent<HTMLAnchorElement>): void => {
|
||||
Router.setRoute(props.target);
|
||||
e.preventDefault();
|
||||
};
|
||||
return (
|
||||
<a onClick={onClick} href={props.target} className={cssClass}>
|
||||
<a href={props.target} className={cssClass}>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
class WalletNavBar extends Component<any, any> {
|
||||
private cancelSubscription: any;
|
||||
function WalletNavBar({ current }: { current?: string }) {
|
||||
|
||||
componentWillMount(): void {
|
||||
this.cancelSubscription = Router.onRoute(() => {
|
||||
this.setState({});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.cancelSubscription) {
|
||||
this.cancelSubscription();
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
console.log("rendering nav bar");
|
||||
return (
|
||||
<div className="nav" id="header">
|
||||
<Tab target="/balance">{i18n.str`Balance`}</Tab>
|
||||
<Tab target="/history">{i18n.str`History`}</Tab>
|
||||
<Tab target="/settings">{i18n.str`Settings`}</Tab>
|
||||
<Tab target="/debug">{i18n.str`Debug`}</Tab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="nav" id="header">
|
||||
<Tab target="/popup/balance" current={current}>{i18n.str`Balance`}</Tab>
|
||||
<Tab target="/popup/history" current={current}>{i18n.str`History`}</Tab>
|
||||
<Tab target="/popup/settings" current={current}>{i18n.str`Settings`}</Tab>
|
||||
<Tab target="/popup/debug" current={current}>{i18n.str`Debug`}</Tab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,7 +94,7 @@ function EmptyBalanceView(): JSX.Element {
|
||||
return (
|
||||
<i18n.Translate wrap="p">
|
||||
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>
|
||||
);
|
||||
}
|
||||
@ -494,7 +414,7 @@ function WalletHistory(props: any): JSX.Element {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{txs.map((tx,i) => (
|
||||
{txs.map((tx, i) => (
|
||||
<TransactionItem key={i} tx={tx} />
|
||||
))}
|
||||
</div>
|
||||
@ -525,7 +445,7 @@ async function confirmReset(): Promise<void> {
|
||||
if (
|
||||
confirm(
|
||||
"Do you want to IRREVOCABLY DESTROY everything inside your" +
|
||||
" wallet and LOSE ALL YOUR COINS?",
|
||||
" wallet and LOSE ALL YOUR COINS?",
|
||||
)
|
||||
) {
|
||||
await wxApi.resetDb();
|
||||
@ -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>(
|
||||
undefined,
|
||||
);
|
||||
@ -671,19 +591,29 @@ function WalletPopup(): JSX.Element {
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<WalletNavBar />
|
||||
<Match>{({ path }: any) => <WalletNavBar current={path} />}</Match>
|
||||
<div style={{ margin: "1em" }}>
|
||||
<Router>
|
||||
<WalletBalanceView route="/balance" default />
|
||||
<WalletSettings route="/settings" />
|
||||
<WalletDebug route="/debug" />
|
||||
<WalletHistory route="/history" />
|
||||
<Route path={Pages.balance} component={WalletBalanceView} />
|
||||
<Route path={Pages.settings} component={WalletSettings} />
|
||||
<Route path={Pages.debug} component={WalletDebug} />
|
||||
<Route path={Pages.history} component={WalletHistory} />
|
||||
</Router>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function createPopup(): JSX.Element {
|
||||
return <WalletPopup />;
|
||||
enum Pages {
|
||||
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
|
||||
}
|
||||
|
@ -26,18 +26,22 @@ import {
|
||||
ApplyRefundResponse,
|
||||
Amounts,
|
||||
} from "@gnu-taler/taler-util";
|
||||
// import { h } from 'preact';
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
|
||||
function RefundStatusView(props: { talerRefundUri: string }): JSX.Element {
|
||||
const [applyResult, setApplyResult] = useState<ApplyRefundResponse|undefined>(undefined);
|
||||
interface Props {
|
||||
talerRefundUri?: string
|
||||
}
|
||||
|
||||
export function RefundStatusView({ talerRefundUri }: Props): JSX.Element {
|
||||
const [applyResult, setApplyResult] = useState<ApplyRefundResponse | undefined>(undefined);
|
||||
const [errMsg, setErrMsg] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (!talerRefundUri) return;
|
||||
const doFetch = async (): Promise<void> => {
|
||||
try {
|
||||
const result = await wxApi.applyRefund(props.talerRefundUri);
|
||||
const result = await wxApi.applyRefund(talerRefundUri);
|
||||
setApplyResult(result);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@ -46,10 +50,14 @@ function RefundStatusView(props: { talerRefundUri: string }): JSX.Element {
|
||||
}
|
||||
};
|
||||
doFetch();
|
||||
}, [props.talerRefundUri]);
|
||||
}, [talerRefundUri]);
|
||||
|
||||
console.log("rendering");
|
||||
|
||||
if (!talerRefundUri) {
|
||||
return <span>missing taler refund uri</span>;
|
||||
}
|
||||
|
||||
if (errMsg) {
|
||||
return <span>Error: {errMsg}</span>;
|
||||
}
|
||||
|
@ -26,7 +26,11 @@ import { AmountView } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
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 [prepareTipResult, setPrepareTipResult] = useState<
|
||||
PrepareTipResult | undefined
|
||||
@ -35,6 +39,7 @@ function TalerTipDialog({ talerTipUri }: { talerTipUri: string }): JSX.Element {
|
||||
const [tipIgnored, setTipIgnored] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!talerTipUri) return;
|
||||
const doFetch = async (): Promise<void> => {
|
||||
const p = await wxApi.prepareTip({ talerTipUri });
|
||||
setPrepareTipResult(p);
|
||||
@ -54,6 +59,10 @@ function TalerTipDialog({ talerTipUri }: { talerTipUri: string }): JSX.Element {
|
||||
setTipIgnored(true);
|
||||
};
|
||||
|
||||
if (!talerTipUri) {
|
||||
return <span>missing tip uri</span>;
|
||||
}
|
||||
|
||||
if (tipIgnored) {
|
||||
return <span>You've ignored the tip.</span>;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ function Diagnostics(): JSX.Element | null {
|
||||
<p>
|
||||
Your wallet database is outdated. Currently automatic migration is
|
||||
not supported. Please go{" "}
|
||||
<PageLink pageName="reset-required.html">here</PageLink> to reset
|
||||
<PageLink pageName="/reset-required">here</PageLink> to reset
|
||||
the wallet database.
|
||||
</p>
|
||||
) : null}
|
||||
@ -99,38 +99,38 @@ function Diagnostics(): JSX.Element | null {
|
||||
return <p>Running diagnostics ...</p>;
|
||||
}
|
||||
|
||||
export function PermissionsCheckbox(): JSX.Element {
|
||||
const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState(
|
||||
false,
|
||||
);
|
||||
async function handleExtendedPerm(): Promise<void> {
|
||||
let nextVal: boolean | undefined;
|
||||
if (extendedPermissionsEnabled) {
|
||||
const granted = await new Promise<boolean>((resolve, reject) => {
|
||||
// We set permissions here, since apparently FF wants this to be done
|
||||
// as the result of an input event ...
|
||||
getPermissionsApi().request(extendedPermissions, (granted: boolean) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error("error requesting permissions");
|
||||
console.error(chrome.runtime.lastError);
|
||||
reject(chrome.runtime.lastError);
|
||||
return;
|
||||
}
|
||||
console.log("permissions granted:", granted);
|
||||
resolve(granted);
|
||||
});
|
||||
|
||||
async function handleExtendedPerm(isEnabled: boolean, setEnable: (v:boolean) => void): Promise<void> {
|
||||
let nextVal: boolean | undefined;
|
||||
|
||||
if (!isEnabled) {
|
||||
const granted = await new Promise<boolean>((resolve, reject) => {
|
||||
// We set permissions here, since apparently FF wants this to be done
|
||||
// as the result of an input event ...
|
||||
getPermissionsApi().request(extendedPermissions, (granted: boolean) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error("error requesting permissions");
|
||||
console.error(chrome.runtime.lastError);
|
||||
reject(chrome.runtime.lastError);
|
||||
return;
|
||||
}
|
||||
console.log("permissions granted:", granted);
|
||||
resolve(granted);
|
||||
});
|
||||
const res = await wxApi.setExtendedPermissions(granted);
|
||||
console.log(res);
|
||||
nextVal = res.newValue;
|
||||
} else {
|
||||
const res = await wxApi.setExtendedPermissions(false);
|
||||
console.log(res);
|
||||
nextVal = res.newValue;
|
||||
}
|
||||
console.log("new permissions applied:", nextVal);
|
||||
setExtendedPermissionsEnabled(nextVal ?? false);
|
||||
});
|
||||
const res = await wxApi.setExtendedPermissions(granted);
|
||||
nextVal = res.newValue;
|
||||
} else {
|
||||
const res = await wxApi.setExtendedPermissions(false);
|
||||
nextVal = res.newValue;
|
||||
}
|
||||
console.log("new permissions applied:", nextVal);
|
||||
setEnable(nextVal ?? false);
|
||||
}
|
||||
|
||||
export function PermissionsCheckbox(): JSX.Element {
|
||||
const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function getExtendedPermValue(): Promise<void> {
|
||||
const res = await wxApi.getExtendedPermissions();
|
||||
@ -138,11 +138,12 @@ export function PermissionsCheckbox(): JSX.Element {
|
||||
}
|
||||
getExtendedPermValue();
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
checked={extendedPermissionsEnabled}
|
||||
onChange={() => handleExtendedPerm()}
|
||||
onChange={() => handleExtendedPerm(extendedPermissionsEnabled, setExtendedPermissionsEnabled) }
|
||||
type="checkbox"
|
||||
id="checkbox-perm"
|
||||
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 (
|
||||
<>
|
||||
<p>Thank you for installing the wallet.</p>
|
||||
|
@ -34,12 +34,14 @@ import {
|
||||
import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util";
|
||||
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 [selectedExchange, setSelectedExchange] = useState<
|
||||
string | undefined
|
||||
>(undefined);
|
||||
const talerWithdrawUri = props.talerWithdrawUri;
|
||||
const [cancelled, setCancelled] = useState(false);
|
||||
const [selecting, setSelecting] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState<string | undefined>("");
|
||||
@ -52,10 +54,9 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!talerWithdrawUri) return
|
||||
const fetchData = async (): Promise<void> => {
|
||||
const res = await getWithdrawalDetailsForUri({
|
||||
talerWithdrawUri: props.talerWithdrawUri,
|
||||
});
|
||||
const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
|
||||
setDetails(res);
|
||||
if (res.defaultExchangeBaseUrl) {
|
||||
setSelectedExchange(res.defaultExchangeBaseUrl);
|
||||
@ -64,6 +65,10 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
fetchData();
|
||||
}, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]);
|
||||
|
||||
if (!talerWithdrawUri) {
|
||||
return <span>missing withdraw uri</span>;
|
||||
}
|
||||
|
||||
if (!details) {
|
||||
return <span>Loading...</span>;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ export function ProgressButton({isLoading, ...rest}: LoadingButtonProps): JSX.El
|
||||
export function PageLink(
|
||||
props: { pageName: string, children?: ComponentChildren },
|
||||
): JSX.Element {
|
||||
const url = chrome.extension.getURL(`/${props.pageName}`);
|
||||
const url = chrome.extension.getURL(`/static/popup.html#/${props.pageName}`);
|
||||
return (
|
||||
<a
|
||||
className="actionLink"
|
||||
|
@ -276,7 +276,7 @@ try {
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
console.log("onInstalled with reason", details.reason);
|
||||
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 });
|
||||
}
|
||||
});
|
||||
@ -311,7 +311,7 @@ function headerListener(
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/withdraw.html",
|
||||
"/static/popup.html#/withdraw",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -320,7 +320,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerPay:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/pay.html",
|
||||
"/static/popup.html#/pay",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -329,7 +329,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerTip:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/tip.html",
|
||||
"/static/popup.html#/tip",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -338,7 +338,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerRefund:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/refund.html",
|
||||
"/static/popup.html#/refund",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -6,7 +6,7 @@
|
||||
|
||||
body {
|
||||
min-height: 20em;
|
||||
width: 30em;
|
||||
/* width: 30em; */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 800px;
|
||||
@ -183,3 +183,58 @@ input[type="radio"] {
|
||||
text-align: center;
|
||||
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;
|
||||
}
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -216,12 +216,14 @@ importers:
|
||||
'@rollup/plugin-replace': ^2.3.4
|
||||
'@testing-library/preact': ^2.0.1
|
||||
'@types/chrome': ^0.0.128
|
||||
'@types/history': ^4.7.8
|
||||
'@types/jest': ^26.0.23
|
||||
'@types/node': ^14.14.22
|
||||
ava: 3.15.0
|
||||
babel-plugin-transform-react-jsx: ^6.24.1
|
||||
enzyme: ^3.11.0
|
||||
enzyme-adapter-preact-pure: ^3.1.0
|
||||
history: 4.10.1
|
||||
jest: ^26.6.3
|
||||
jest-preset-preact: ^4.0.3
|
||||
preact: ^10.5.13
|
||||
@ -251,12 +253,14 @@ importers:
|
||||
'@rollup/plugin-replace': 2.3.4_rollup@2.37.1
|
||||
'@testing-library/preact': 2.0.1_preact@10.5.13
|
||||
'@types/chrome': 0.0.128
|
||||
'@types/history': 4.7.8
|
||||
'@types/jest': 26.0.23
|
||||
'@types/node': 14.14.22
|
||||
ava: 3.15.0
|
||||
babel-plugin-transform-react-jsx: 6.24.1
|
||||
enzyme: 3.11.0
|
||||
enzyme-adapter-preact-pure: 3.1.0_enzyme@3.11.0+preact@10.5.13
|
||||
history: 4.10.1
|
||||
jest: 26.6.3
|
||||
jest-preset-preact: 4.0.3_669f037bdb6c36f0a67e918c516dafdd
|
||||
preact-cli: 3.0.5_c069246dc1d99535ac277c76f8ef56e0
|
||||
@ -2178,6 +2182,10 @@ packages:
|
||||
resolution: {integrity: sha512-IG8AE1m2pWtPqQ7wXhFhy6Q59bwwnLwO36v5Rit2FrbXCIp8Sk8E2PfUCreyrdo17STwFSKDAkitVuVYbpEHvQ==}
|
||||
dev: true
|
||||
|
||||
/@types/history/4.7.8:
|
||||
resolution: {integrity: sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==}
|
||||
dev: true
|
||||
|
||||
/@types/istanbul-lib-coverage/2.0.3:
|
||||
resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==}
|
||||
dev: true
|
||||
@ -6569,6 +6577,17 @@ packages:
|
||||
resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==}
|
||||
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:
|
||||
resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=}
|
||||
dependencies:
|
||||
@ -10818,6 +10837,10 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/resolve-pathname/3.0.0:
|
||||
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
|
||||
dev: true
|
||||
|
||||
/resolve-url/0.2.1:
|
||||
resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=}
|
||||
deprecated: https://github.com/lydell/resolve-url#deprecated
|
||||
@ -12097,6 +12120,14 @@ packages:
|
||||
resolution: {integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=}
|
||||
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:
|
||||
resolution: {integrity: sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=}
|
||||
dev: true
|
||||
@ -12674,6 +12705,10 @@ packages:
|
||||
engines: {node: '>= 0.10'}
|
||||
dev: true
|
||||
|
||||
/value-equal/1.0.1:
|
||||
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
|
||||
dev: true
|
||||
|
||||
/vary/1.1.2:
|
||||
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
Loading…
Reference in New Issue
Block a user